django(8)QuerySet API

模型.objects:

这个对象是django.db.models.manager.Manager的对象,这个类是一个空壳类,他上面的所有方法都是从QuerySet这个类上面拷贝过来的。因此我们只要学会了QuerySet,这个objects也就知道该如何使用了。
Manager源码解析:

class_name = "BaseManagerFromQuerySet"

class_dict = {
    '_queryset_class': QuerySet
}

class_dict.update(cls._get_queryset_methods(QuerySet))

# type动态的时候创建类
# 第一个参数是用来指定创建的类的名字。创建的类名是:BaseManagerFromQuerySet
# 第二个参数是用来指定这个类的父类。
# 第三个参数是用来指定这个类的一些属性和方法
return type(class_name,(cls,),class_dict)

_get_queryset_methods:这个方法就是将QuerySet中的一些方法拷贝出来

filter/exclude/annotate:过滤/排除满足条件的/给模型添加新的字段。

order_by:

# 根据创建的时间正序排序
articles = Article.objects.order_by("create_time")
# 根据创建的时间倒序排序
articles = Article.objects.order_by("-create_time")
# 根据作者的名字进行排序
articles = Article.objects.order_by("author__name")
# 首先根据创建的时间进行排序,如果时间相同,则根据作者的名字进行排序
articles = Article.objects.order_by("create_time",'author__name')

一定要注意的一点是,多个order_by,会把前面排序的规则给打乱,而使用后面的排序方式。比如以下代码:

articles = Article.objects.order_by("create_time").order_by("author__name")

他会根据作者的名字进行排序,而不是使用文章的创建时间。
当然,也可以在模型定义的在Meta类中定义ordering来指定默认的排序方式。示例代码如下:

    class Meta:
        db_table = 'book_order'
        ordering = ['create_time','-price']

还可以根据annotate定义的字段进行排序。比如要实现图书的销量进行排序,那么示例代码如下:

books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums")
    for book in books:
        print('%s/%s'%(book.name,book.order_nums))

values:

有时候我们在表中查找数据的时候,并不是想把所有的字段都提取出来。我们有可能只想要其中的几个字段,这时候就可以使用values来实现,需要几个字段,就把这几个字段的名字传递到这个方法中,示例代码如下:

books = Book.objects.values('id', 'name')

values的返回值同样是一个QuerySet对象,但其中不在是模型,而是字典。

如果我们想要提取的是这个模型上关联的对象属性,那么也是可以的,查找顺序跟filter的用法是一样的,示例代码如下:

books = Book.objects.values('id', 'name', 'author__name')

以上将会提取authorname字段,如果我们不想使用这个名字。那么可以使用关键字 参数,示例代码如下:

books = Book.objects.values("id", "name", "author_name=F('author__name')")

注意:自定义的名字,不能和模型本身拥有的字段一样,比如以上author_name取名author就会报错,因为Book上本身就有author字段名字。

values中,也可以使用聚合函数来形成一个新的字段,比如我想要获取每本图书的销量,那么示例代码如下:

books = Book.objects.values('id', 'name', order_nums=Count('bookorder'))

如果调用values方法时,没有传递任何参数,那么会获取这个模型上的所有的字段以及对应的值形成的字典,示例代码如下:

books = Book.objects.values()

那么books中的值如下:

{'id': 1, 'name': '三国演义', 'pages': 987, 'price': 108.0, 'rating': 3.9, 'author_id': 3, 'publisher_id': 1}

values_list

values是一样的作用,只不过这个方法返回的QuerySet中,装的不是字典,而是元组,示例代码如下:

 books = Book.objects.values_list('id', 'name')

那么以上代码返回的结果时:

(1, '三国i演义')

如果给values_list只指定一个字段,那我们可以指定flat=True,这样返回回来的结果就不在是一个元组,而是这个字段的值,示例代码如下:

books = Book.objects.values_list('name', flat=True)

那么以上返回的结果是:

'三国演义'

一点要注意的是,flat只能用在一个字段的情况下,否则就会报错。

all:

这个方法简单的返回一个QuerySet对象,这个QuerySet对象没有经过任何的修改(比如:过滤等)

select_related:

在查找某个表的数据的时候,可以一次性把相关联的其他表的数据都提取出来,这样可以在以后访问相关联的表的数据的时候,不用再次查询数据库,可以节省一些开销。示例代码如下:

books = Book.objects.select_related("author", ''publisher")
for book in books:
    print(book.author.name)
    # 因为在提取book时后,使用了select_related,
    # 那么以后再访问book.author的时候,不会再次项数据库重新发起查询

注意:这个方法只能用在外键关联的对象上,对于那种多对多、或者多对一的情况,不能使用它来实现,而应该使用prefetch_related来实现。

prefetch_related:

这个方法类似于select_related方法,也是用来在查询语句的时候,提前将指定的数据找出来,只不过这个方法用来解决多对多,或者多对一的情况,这个方法产生两个查询语句。所以,若果在这个方法中查询使用外键关联的模型时,也会产生两个查询语句,因此如果查询的是外键关联的模型建议使用select_related方法。在查询多对多或者多对一的关联对象的时候,在使用模型怎么访问这个多对多,那么就在这个方法中传递什么字符串,比如要获取图书上的所有订单,那么示例代码如下:

books = Book.objects.preftch_related(''bookorder_set")

需要注意的是:在使用preftch_related查找出来的bookorder_ser,建议不要再对他进行任何操作,不如filter,不然又会产生N多查询语句,比如以下代码是不对的:

books = Book.objects.preftch_related(''bookorder_set")
for book in books:
    print('='*30)
    print(book.name)
    orders = book.bookorder_set.filter(price__gte=90)
    for order in orders:
        print(order.id)

那么如果确实像要对预先查找的集合进行操作,那么我们可以使用django.db.models.Prefetch来完成,示例代码如下:

form django.db.models import Prefetch
# 先使用Prefetch把查找的条件写好,再放到prefetch_related中
prefetch = Prefetch("bookorder_set", queryset=BookOrder.objects.filter(price__gte=90))
books = Book.objects.preftch_related(prefetch)
for book in books:
    print('='*30)
    print(book.name)
    orders = book.bookorder_set.all()
    for order in orders:
        print(order.id)

defer和only:

这个方法都会返回一个QuerySet对象,并且这个QuerySet中装的都是模型,而不是字典。
1、defer: 这个方法用来告诉ORM, 在查询某个模型的时候,过滤掉某些字段。
2、only: 这个方法用来告诉ORM,在查询某个模型的时候,只提取某几个字段。
注意:使用defer或者only,如果没有提取这个字段,那么后面使用这个字段,会重新发起一次请求,因此要谨慎使用。

get:

获取满足条件的值,这个方法给定的条件只能匹配到一条数据,如果匹配到多条数据或者没有数据,它都会报错,因此使用时,都是根据主键为条件来匹配的。

create:

可以创建一条数据,并且将这条数据保存到数据库中,以下代码是等价的。

publisher = Publisher(name='xxxx出版社')
publisher.save()
# 等价于下面这句
Publisher.objects.create(name='xxxx出版社')

get_or_create:

如果给定的条件有数据,那么就会把这个数据给提取出来,如果给定的条件没有数据,就会先创建数据,然后把数据给返回回来。

bulk_creat:

一次性创建多个数据,示例代码如下:

publisher = Publisher.objects.bulk_create([
    Publisher(name='123出版社'),
    Publisher(name='456出版社')
])

count:

获取提取的数据个数,如果想要知道总共有多少条数据,那么建议使用count,而不是使用len(books)这种,因为Count在底层是使用select count(*)来实现的,这种方式比使用len函数更加高效。

first 和 last

返回QuerySet中第一和最后一条数据

exists:

判断某个条件的数据是否存在,如果判断某个条件的元素是否存在,建议使用exists,这个比使用count或者直接判断QuerySet更有效的多。示例代码如下:

if Article.objects.filter(title__contains='hello').exists():
    print(True)
# 比使用count更高效
if Article.objects.filter(title__contains='hello').count() > 0:
    print(True)
# 也比直接判断QuerySet更高效
if Article.objects.filter(title__contains='hello'):
    print(True)

distinct:

去掉那些重复的数据,这个方法如果底层数据库使用的是MySQL,那么不能传递任何参数,比如想要提取所有销售价格超过80元的图书,并且删除掉那些重复的,那么可以使用distinct来帮我们实现,示例代码如下:

books = Book.objects.filter(bookorder__price__gte=80).distinct()

需要注意的是,如果distinct之前使用了order_by,那么order_by会提取order_by中指定的字段,因此,再使用distinct1就会根据多个字段来进行唯一化,就不会把那些重复的数据删除掉,示例代码如下:

orders = BookOrder.objects.order_by('create_time').values('book_id').distinct()
# 那么以上代码因为使用了order_by,即使使用了distinct,也会把重复的book_id提取出来

update和delete:

一次性可以把所有的数据都更新,
一次性可以把满足条件的数据都删除掉,但需要注意的删除数据的时候,要注意on_delete指定的处理方式。

切片操作:

有时候我们查找数据,有可能只需要其中的一部分,那么这时候可以使用切片操作来帮助我们完成。QuerySet使用切片操作就跟列表使用切片操作一样,示例代码如下:

books = Book.objects.all()[1:3]
for book in books:
    print(book)

切片操作并不是把所有的数据从数据库中提取出来再做切片操作,而是在数据库层面使用LIMIT和OFFSET来帮助我们完成。

什么时候 Django 会将 QuerySet 转换为 SQL 去执行:

1、迭代
2、使用步长做切片操作
3、调用len函数
4、调用list函数:将一个QuerySet对象转换为list对象也会立即执行SQL语句。
5、判断:如果对某个QuerySet进行片段,也会立马执行 SQL 语句。

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

推荐阅读更多精彩内容