Django 学习笔记之模型(下)

题图:by click_vision from Instagram

上篇文章讲解了 Django 如何创建模型,本文将继续讲解如何对模型进行增删改查操作。

1 前言

当我们建立好数据模型,Django 会自动为我们生成一套数据库接口相关的接口。这套接口称为 QuerySet API。为什么叫 QuerySet ? 因为从数据库中查询出来的结果一般是一个集合,这个集合叫做 QuerySet。

为了方便理解,我继续使用上篇文章的例子。另外方便我们在打印对象信息时,能得到对象的信息。所以我们需要对之前的代码做下修改。分别为每个模型类添加一个方法 unicode()unicode() 方法告诉 Python 如何将对象以 Unicode 的方式显示出来。 为以上三个模型添加 unicode() 函数后,就可以看到效果了:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=40)
    email = models.EmailField()

    def __unicode__(self):
        return self.name

class Publisher(models.Model): 
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    publication_date = models.DateField()

    def __unicode__(self):
        return self.title

unicode() 方法可以进行任何处理来返回一个 unicode 对象。总所周知,Python 内部对字符串都是使用 Unicode 来保存的。不像字符串那样,有什么 UTF-8、GB2312 等编码。所以我们在Python 中处理 Unicode 对象的时候,你可以直接将它们混合使用和互相匹配而不必去考虑编码细节。

2 创建对象

为了更加直观的操作数据库,我使用 Django 的 API 来讲解。在项目的目录下,打开终端执行以下命令:

python manage.py shell

然后在终端中依次输入以下代码:

# 前面的 >>> 是终端自带的
>>> from demo.models import Publisher
>>> p = Publisher(name='清华大学出版社', address='北京')
>>> p.save()

接着使用 Pycharm 的 Database 功能,查询 demo_publisher 表,你会发现新增一条数据。


点击查看大图

现在来说说刚才发生了什么。第二行代码,即初始化一个 Publisher 实例, 这个实例并没有对数据库做修改。只有调用了 save() 函数,记录才会提交到数据库。所以, 使用这种方法创建实例,最后一定要调用 save() 函数。

另外上述方法来创建实例,另外还有 3 种方法:
1)方法2
这种办法可以算是方法 1 的变形。

from demo.models import Publisher
p = Publisher()
p.name='北京大学出版社'
p.address='北京'
p.save()
# 别忘记最后调用 save() 函数

2)方法3
这种办法是用到 objects.create() 函数

from demo.models import Publisher
Publisher.objects.create(name='人民邮电出版社', address='北京')
# 这种办法无需调用 save()

3)方法4
这种办法是用到 objects.get_or_create() 方法。使用这种办法有好处也有坏处。好处是可以防止重复插入;那么坏处就是插入速度要相对慢些,因为它要先查询。

from demo.models import Publisher
Publisher.objects.get_or_create(name='清华大学出版社', address='北京')
# 这种办法无需调用 save()
# 执行结果如下:
(<Publisher: Publisher object (1)>, False)

返回结果跟其他方法返回结果有点不同,它返回是一个元组。第一个是 Publisher 对象;第二个为一个布尔值,如果能新建成功为 True,已经存在则为 Flase。

如果模型中存在有一对多,多对一,多对多的关系,先把相关的对象查询出来或者创建出来,才能创建该模型。例如我们要创建 Book 对象,首先要创建 Author 和 Publisher 对象。具体代码如下:

import datetime
from demo.models import Publisher, Author, Book
# 获取 Publisher 对象
pub = Publisher.objects.get(name='清华大学出版社')
# 创建并获取 Publisher 对象
Author.objects.create(name='令狐冲', email='makeTheFoxRush@xx.com')
aut = Author.objects.get(name='令狐冲')
# 创建 Book 对象, 要注意添加 id 字段
book = Book(id=None,title='令狐传', publisher=pub, publication_date=datetime.date.today())
# 一定要先保存数据到数据库,才能添加多对多关系的对象 author
book.save()
book.authors.add(aut)
book.save()

3 查询对象

Django 提供在查询数据功能方面做了很多优化工作, 这让我们查询数据有千万种方法。

3.1 查询单条数据

其实在上面的例子, 我们已经运用到单条数据功能。没错,就是使用 get() 方法来获取单条数据。

pub = Publisher.objects.get(name='清华大学出版社')

3.2 查询多条数据

如果现在我们需要查询符合某个条件的数据,get() 只能返回一条数据,无法满足我们的需求。所以我们需要用到过滤器 filter。

from demo.models import Publisher
Publisher.objects.filter(address='北京')
# 运行结果
<QuerySet [<Publisher: Publisher object (1)>, <Publisher: Publisher object (2)>, <Publisher: Publish
er object (3)>]>

查询多条数据的返回结果为 QuerySet,这部分等会继续讲解。

另外 filter 还支持其他过滤条件,例如

# 正则表达式
Publisher.objects.filter(address__regex='^北京')

# 部分查询
# 查询地址中包含'北'字的出版社
Publisher.objects.filter(address__contains='北')

# 还有很多不一一例举
# 如果上述条件的前面有个字母 'i', 表示不区分大小写

# 正则表达式,但不区分大小写
Publisher.objects.filter(address__iregex='^beijing')

# 部分查询,但不区分大小写
Publisher.objects.filter(address__icontains='bei')

如果要查询全部数据或者一段连续区间的数据,可以使用 all() 函数

# 查询所有数据
Publisher.objects.all()

# 查询一段连续区间的数据
Publisher.objects.all()[:3]

[:3] 是用到 Python 中的切片操作。因为上限从 0 开始可以忽略不写,所以它等同于 [0:3]。查询出来结果没有包含上限的值,即下标为 3 的值。[:3] 只查询下标为 0, 1 ,2 的数据。

但是这里比较特殊,QuerySet 对象的 id 是从 1 开始的,所以 [:3] 表示 [1:3], 返回 id 为 1, 2, 3 的对象。 另外,这种切片操作时可以节约内存的。

4 更新数据

更新数据操作,一般是在查询数据后才执行。

4.1 更新单条数据

更新单条数据也有两种方法,其中一种的用法跟使用方法 2 创建对象类似,另一种则是使用 update_or_create() 。具体代码如下:

# 方法1
from demo.models import Publisher
p = Publisher.objects.get(name="北京大学出版社")
p.name='上海大学出版社'
p.address='上海'
p.save()
# 别忘记最后调用 save() 方法

# 方法2
p = Publisher.objects.update_or_create(name='北京大学出版社', address='上海')

update_or_create() 方法是以模型的其中一个属性去匹配,如果数据库中有匹配数据就更新后面的值,否则则创建新的数据。

4.2 更新多条数据

批量更新多条数据,一般是在 all(),filter() 后面执行 update() 函数

from demo.models import Publisher
p = Publisher.objects.filter(address='北京').update(address='北京海淀')

5 删除数据

删除单条数据或多条数据,用法跟更新数据类似。具体就不逐一展开讲解了,大概说下用法即可。删除单条数据,获取数据,然后调用 delete() 函数。删除多条数据,同样在获取数据后调用 delete() 函数。

6 QuerySet 用法

前面讲到,使用 all(),filter() 查询多条数据,返回的结果是一个 QuerySet 对象。它不是个列表,但是可以使用** list() 将其转变为列表**。

from demo.models import Publisher
pubs = Publisher.objects.all()
pub_list = list(pubs)

6.1 可跌代性

QuerySet 是一个可迭代的对象。因此,可以使用 for 循环来遍历。代码如下:

# 在 views.py 中
from django.http import HttpResponse
from django.shortcuts import render
from demo.models import Publisher

# Create your views here.
def index(request):
    pubs = Publisher.objects.all()
    for p in pubs:
        print(p.name)
    return HttpResponse("Hello")

你会在 Pycharm 的控制台上看到查询的数据。

6.2 支持排序

QuerySet 支持对查询结果排序。例如将出版社按照名称来排序,

# 在 views.py 中
from django.http import HttpResponse
from django.shortcuts import render
from demo.models import Publisher

# Create your views here.
def index(request):
    pubs = Publisher.objects.all().order_by('name')
    print(=== 正序排序 ===)
    for p in pubs:
        print(p.name)

    # 在 column name 前加一个负号,可以实现倒序
    repubs = Publisher.objects.all().order_by('-name')   
    print(=== 反序排序 ===)
    for p in repubs:
        print(p.name)
    return HttpResponse("Hello")

6.3 组合查询

QuerySet 还支持跟 SQL 语法中的组合查询,具体用法如下:

# 查询结果中同时满足 name=清华大学出版社 和 address=上海, 这两个条件
Publisher.objects.filter(name="清华大学出版社").filter(address="上海")

# 查询结果中同时满足 name=清华大学出版社 和 address 不是上海, 这两个条件
# exclude() 函数排除指定的内容
Publisher.objects.filter(name="清华大学出版社").exclude(address="上海")

6.4 其他

前面说到切片操作的区间是从整数到无穷大的,在Python 语法中还有负查询,即区间是从负无穷大到 0。可惜的是 QuerySet 是不支持负查询。

但是也有替代方法

# 使用 reverse() 将 QuerySet 的顺序倒置下
# 再使用正查询, 下面的例子表示查询最后两条数据。
Publisher.objects.all().reverse()[:2] 

如果还要获取 QuerySet 里面存放对象的个数,可以使用 count() 函数来查询数量。内部实现是用执行 SELECT COUNT() SQL 语句*。

count = Publisher.objects.all().count()
print('count == ', count)

源码地址

4 写在最后

我新建一个 Python Web 学习交流 QQ 群,群号:701534112。欢迎大家加群,一起交流,一起学习,一起进步。


Python Web 学习交流群

往前 Django 学习笔记文章
Django 学习笔记之环境搭建
Django 学习笔记之初始
Django 学习笔记之视图与URL配置
Django 学习笔记之模板
Django 学习笔记之模型(上)


本文原创发布于微信公众号「极客猴」,欢迎关注第一时间获取更多原创分享

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容