查询集(QuerySet)方法

本文 model 设置如下:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

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

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):              # __unicode__ on Python 2
        return self.headline




查询集的方法

  • filter()
    返回一个新的 QuerySet,返回与查询条件匹配的对象。

  • exclude()
    返回一个新的 QuerySet,返回查询条件不匹配的对象。

  • annotate()
    (待补充)




查询集排序

默认情况下,QuerySet 根据模型 Meta 类的 ordering 选项排序。要使用特定的排序方法时可以用 order_by()

# 按 pub_date 降序排列
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date')

#  按 pub_date 降序排列,再按 headline 升序排列
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

若要随机排序,请使用 " ? ":

Entry.objects.order_by('?')

注:order_by('?') 查询可能耗费资源且很慢。

涉及外键的排序

若要按照另外一个模型中(即:外键或其他对应关系)的字段排序,可以在字段的名称后面跟上两个下划线(__),再跟上新模型中的字段的名称,例如:

# 按照所属的博客的名称排序
Entry.objects.order_by('blog__name')

如果单纯用另一个模型的小写名来排序,相当于是按照该模型的主键来排序:

# 这两种写法等价
Entry.objects.order_by('blog')
Entry.objects.order_by('blog__id')
反向排序

使用 reverse() 方法可以使一个排好序的查询集反向排序

e = Entry.objects.order_by('blog_id')
# 反向排序
e.reverse()




distinct

(待补充)

values

返回一个ValuesQuerySet —— QuerySet 的一个子类,对它进行迭代操作时返回字典。

e = Entry.objects.order_by('blog')
v = e.values()

>>> type(v)
<class 'django.db.models.query.QuerySet'>

# 对它进行迭代操作时将返回一个字典
>>> type(v[0])
<class 'dict'>

values() 接收可选的位置参数 *fields,指定输出哪些字段:

>>> e.values('id', 'headline')
<QuerySet [{'headline': 'ringo 教你打鼓', 'id': 3}, {'headline': 'John 和 Paul
教你作曲', 'id': 4}, {'headline': 'George 教你弹吉他', 'id': 5}]>

如果 values() 的参数是一个ForeignKey,默认的 values() 调用返回的字典将会是一个叫做 母表的 id :

>>> Entry.objects.values('blog')
# 返回的是母表的 id
<QuerySet [{'blog': 2}, {'blog': 2}, {'blog': 2}]>

# 这种写法与上面的等价
>>> Entry.objects.values('blog_id')
values_list

与 values() 类似,只是在迭代时返回的是元组而不是字典:

>>> e = Entry.objects.values_list('id', 'headline')

>>> type(e)
<class 'django.db.models.query.QuerySet'>

# 返回一个元组
>>> type(e[0])
<class 'tuple'>

当 flat 参数为True 时,它表示返回的结果为单个值而不是元组:

>>> e = Entry.objects.values_list('id', flat=True).order_by('id')

>>> e[0]
1

>>> type(e[0])
<class 'int'>

注意:如果有多个字段,传递 flat 将发生错误。

如果你不传递任何值给 values_list(),它将返回模型中的所有字段。




dates
dates(field, kind, order='ASC')

返回 DateQuerySet - QuerySet,其计算结果为 datetime.date 对象。

field 应为模型的 DateField 的名称。 kind 应为 "year"、"month" 或 "day"。

  • "year" 表示精确到年
  • “month” 表示精确到月
  • “day” 表示精确到日

order(默认为“ASC”)应为'ASC'或'DESC'。
注:asc 是升序排列,desc 是降序排列。

例子:

# 返回值精确到年,因为所有 entry 的 pub_date 都是 2017年,所以只有一个返回值
>>> Entry.objects.dates('pub_date', 'year')
<QuerySet [datetime.date(2017, 1, 1)]>

# 返回值精确到月
>>> Entry.objects.dates('pub_date', 'month')
<QuerySet [datetime.date(2017, 2, 1)]>

# 精确到日,默认为升序
>>> Entry.objects.dates('pub_date', 'day')
<QuerySet [datetime.date(2017, 2, 1), datetime.date(2017, 2, 2)]>

# 精确到日,降序排列
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
<QuerySet [datetime.date(2017, 2, 2), datetime.date(2017, 2, 1)]>

# 查询某篇博文的 pub_date
>>> e = Entry.objects.filter(headline='George 教你弹吉他')
>>> e.dates('pub_date', 'day')
<QuerySet [datetime.date(2017, 2, 1)]>
datetimes

(待补充)




None

调用 none() 将创建一个从不返回任何对象的查询集,并且在访问结果时不会执行任何查询。

qs.none() 查询集是 EmptyQuerySet 的一个实例。

>>> Entry.objects.none()
[]

>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True
all()

返回全部




select_related

返回一个QuerySet。当使用 select_related 去查询带外键关系的对象时,它会预加载相关的数据表,以后再查询外键关系时就不用再去请求数据库。

下面的例子解释了普通查询和 select_related() 查询的区别:

  • 普通查询:
# 请求数据库
e = Entry.objects.get(id=5)

# 再次请求数据库
b = e.blog
  • select_related 查询:
# 请求数据库
e = Entry.objects.select_related('blog').get(id=5)

# 无需再请求数据库,因为相关数据已经预加载
b = e.blog

假如有“外键链”,如下所示:

from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(City)

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person)

可以这样调用将缓存关联的 Person 和关联的 City:

b = Book.objects.select_related('author__hometown').get(id=4)
p = b.author         
c = p.hometown       
prefetch_related

(待补充)

参考:http://blog.jobbole.com/74881/




extra

(待补充)




不会返回 QuerySets 的方法
  • get(**kwargs)

  • create(**kwargs)

  • get_or_create(defaults=None, **kwargs)

  • update_or_create(defaults=None, **kwargs)

  • bulk_create(objs, batch_size=None)

bulk_create 方法是用一个列表来批量插入数据:

Entry.objects.bulk_create([
    Entry(headline="Django 1.0 Released"),
    Entry(headline="Django 1.1 Announced"),
    Entry(headline="Breaking: Django is awesome"
)])
  • count()

  • in_bulk(id_list)

获取主键值的列表,并返回一个字典:

# 如果不填参数将获取全部
>>> Entry.objects.in_bulk()
{
3: <Entry: ringo 教你打鼓>,
4: <Entry: John 和 Paul 教你作曲>,
5: <Entry: George 教你弹吉他>
}

>>> Entry.objects.in_bulk([3])
{3: <Entry: ringo 教你打鼓>}

>>> Entry.objects.in_bulk([3,4])
{
3: <Entry: ringo 教你打鼓>, 
4: <Entry: John 和 Paul 教你作曲>
}
  • iterator()

iterator() 方法可以把一个查询集变成一个迭代器:

a = Entry.objects.all()
a = a.iterator()

# 生成一个迭代器
>>> type(a)
generator
  • latest(field_name=None)

按日期返回表中的最的新对象:

# 返回 pub_date 最新的对象
>>> Entry.objects.latest('pub_date')
<Entry: John 和 Paul 教你作曲>
  • earliest(field_name=None)

按日期返回表中的最早的对象:

>>> Entry.objects.earliest('pub_date')
<Entry: ringo 教你打鼓>
  • first()

返回结果集的第一个对象, 当没有找到时返回None。

例:

>>> Entry.objects.first()
<Entry: ringo 教你打鼓>
  • last()

类似 first(),只是返回的是查询集中最后一个对象。

  • aggregate(*args, **kwargs)

(聚合查询,待补充)

  • exists()

判断某 QuerySet 是否存在某个对象,返回 True 或 False。这是一个高效的查询方法,特别是 QuerySet 比较大的时候。

>>> entry = Entry.objects.get(pk=3)
>>> all_entry = Entry.objects.all()
>>> all_entry.filter(pk=entry.pk).exists()
True

>>> some_entry = Entry.objects.filter(pk=4)
>>> some_entry.filter(pk=entry.pk).exists()
False

它将比下面的方法快很多:

if entry in some_queryset:
  ……

若要判断一个 QuerySet 是否包含任何元素,

if some_queryset.exists():

会快于

if some_queryset:
  • update(**kwargs)

更新指定字段,例子:

e = Entry.objects.filter(pk=4)
e.update(rating=1)

# 也可以批量修改查询集里面的全部字段
e = Entry.objects.all()
e.update(rating=1)
  • delete()

删除指定字段,例子:

# 删除指定博客的全部博文
b = Blog.objects.get(pk=1)
Entry.objects.filter(blog=b).delete()

# 删除全部博空
blogs = Blog.objects.all()
blogs.delete(
  • as_manager

(待补充)




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

推荐阅读更多精彩内容