以下的方法不会返回QuerySets,但是作用非常强大,尤其是粗体显示的方法,需要背下来。
方法名 | 解释 |
---|---|
get() | 获取单个对象 |
create() | 创建对象,无需save() |
get_or_create() | 查询对象,如果没有找到就新建对象 |
update_or_create() | 更新对象,如果没有找到就创建对象 |
bulk_create() |
批量创建对象 |
bulk_update() | 批量更新对象 |
count() | 统计对象的个数 |
in_bulk() |
根据主键值的列表,批量返回对象 |
iterator() |
获取包含对象的迭代器 |
latest() | 获取最近的对象 |
earliest() | 获取最早的对象 |
first() | 获取第一个对象 |
last() | 获取最后一个对象 |
aggregate() | 聚合操作 |
exists() | 判断queryset中是否有对象 |
update() | 更新对象 |
delete() | 删除对象 |
as_manager() | 获取管理器 |
explain() | 对数据库操作的解释性信息 |
返回按照查询参数匹配到的单个对象,参数的格式应该符合Field lookups的要求。例如:
Entry.objects.get(id=1) Entry.objects.get(blog=blog, entry_number=1)
如果匹配到的对象个数不只一个的话,触发MultipleObjectsReturned
异常:
Entry.objects.get(name='张伟') # raises Entry.MultipleObjectsReturned
如果根据给出的参数匹配不到对象的话,触发DoesNotExist异常。例如:
Entry.objects.get(id=-999) # raises Entry.DoesNotExist
DoesNotExist异常从django.core.exceptions.ObjectDoesNotExist
继承,可以定位多个DoesNotExist异常。 例如:
from django.core.exceptions import ObjectDoesNotExist try: blog = Blog.objects.get(id=1) entry = Entry.objects.get(blog=blog, entry_number=1) except ObjectDoesNotExist: print("Either the blog or entry doesn't exist.")
如果希望查询器只返回一个对象,则可以使用get(),无需添加任何参数:
entry = Entry.objects.filter(...).exclude(...).get()
在一步操作中同时创建并且保存对象的便捷方法.
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
等于:
p = Person(first_name="Bruce", last_name="Springsteen") p.save(force_insert=True)
参数force_insert
表示强制创建对象。如果model中有一个你手动设置的主键,并且这个值已经存在于数据库中, 调用create()将会失败并且触发IntegrityError因为主键必须是唯一的。如果你手动设置了主键,做好异常处理的准备。
通过kwargs来查询对象的便捷方法(如果模型中的所有字段都有默认值,可以为空),如果该对象不存在则创建一个新对象。
该方法返回一个由(object, created)组成的元组,元组中的object 是一个查询到的或者是被创建的对象, created是一个表示是否创建了新的对象的布尔值。
对于下面的代码:
try: obj = Person.objects.get(first_name='John', last_name='Lennon') except Person.DoesNotExist: obj = Person(first_name='John', last_name='Lennon', birthday=date(2020, 10, 9)) obj.save()
如果模型的字段数量较大的话,这种模式就变的非常不易用了。 上面的示例可以用get_or_create()
重写 :
obj, created = Person.objects.get_or_create( first_name='John', last_name='Lennon', defaults={'birthday': date(2020, 10, 9)}, )
任何传递给get_or_create()
的关键字参数,除了一个可选的defaults,都将传递给get()调用。 如果查找到一个对象,返回一个包含匹配到的对象以及False 组成的元组。
你甚至可以使用filter和Q进行一些比较复杂的查找,如下例所示:
from django.db.models import Q obj, created = Person.objects.filter( Q(first_name='Bob') | Q(first_name='Robert'), ).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})
如果查找到的对象超过一个以上,将引发MultipleObjectsReturned。如果查找不到对象,get_or_create()
将会实例化并保存一个新的对象,返回一个由新的对象以及True组成的元组。新的对象将会按照以下的逻辑创建:
params = {k: v for k, v in kwargs.items() if '__' not in k} params.update({k: v() if callable(v) else v for k, v in defaults.items()}) obj = self.model(**params) obj.save()
它表示从非'defaults' 且不包含双下划线的关键字参数开始。然后将defaults的内容添加进来,覆盖必要的键,并使用结果作为关键字参数传递给模型类。
如果有一个名为defaults__exact
的字段,并且想在get_or_create()
时用它作为精确查询,只需要使用defaults,像这样:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
当你使用手动指定的主键时,get_or_create()
方法与create()
方法有相似的错误行为 。 如果需要创建一个对象而该对象的主键早已存在于数据库中,IntegrityError异常将会被触发。
这个方法假设进行的是原子操作,并且正确地配置了数据库和正确的底层数据库行为。如果数据库级别没有对get_or_create
中用到的kwargs强制要求唯一性(unique和unique_together),方法容易导致竞态条件,可能会有相同参数的多行同时插入。(简单理解,kwargs必须指定的是主键或者unique属性的字段才安全。)
最后建议只在Django视图的POST请求中使用get_or_create(),因为这是一个具有修改性质的动作,不应该使用在GET请求中,那样不安全。
可以通过ManyToManyField属性和反向关联使用get_or_create()
。在这种情况下,应该限制查询在关联的上下文内部。 否则,可能导致完整性问题。
例如下面的模型:
class Chapter(models.Model): title = models.CharField(max_length=255, unique=True) class Book(models.Model): title = models.CharField(max_length=256) chapters = models.ManyToManyField(Chapter)
可以通过Book的chapters字段使用get_or_create()
,但是它只会获取该Book内部的上下文:
>>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError
发生这个错误是因为尝试通过Book “Ulysses”获取或者创建“Chapter 1”,但是它不能,因为它与这个book不关联,但因为title 字段是唯一的它仍然不能创建。
类似前面的get_or_create()
。
通过给出的kwargs来更新对象的便捷方法, 如果没找到对象,则创建一个新的对象。defaults是一个由 (field, value)对组成的字典,用于更新对象。defaults中的值可以是可调用对象(也就是说函数等)。
该方法返回一个由(object, created)组成的元组,元组中的object是一个创建的或者是被更新的对象, created是一个标示是否创建了新的对象的布尔值。
update_or_create
方法尝试通过给出的kwargs 去从数据库中获取匹配的对象。 如果找到匹配的对象,它将会依据defaults 字典给出的值更新字段。
像下面的代码:
defaults = {'first_name': 'Bob'} try: obj = Person.objects.get(first_name='John', last_name='Lennon') for key, value in defaults.items(): setattr(obj, key, value) obj.save() except Person.DoesNotExist: new_values = {'first_name': 'John', 'last_name': 'Lennon'} new_values.update(defaults) obj = Person(**new_values) obj.save()
如果模型的字段数量较大的话,这种模式就变的非常不易用了。 上面的示例可以用update_or_create()
重写:
obj, created = Person.objects.update_or_create( first_name='John', last_name='Lennon', defaults={'first_name': 'Bob'}, )
kwargs中的名称如何解析的详细描述可以参见get_or_create()
。
和get_or_create()
一样,这个方法也容易导致竞态条件,如果数据库层级没有前置唯一性会让多行同时插入。
bulk_create(objs, batch_size=None, ignore_conflicts=False)
以高效的方式(通常只有1个查询,无论有多少对象)将提供的对象列表插入到数据库中:
>>> Entry.objects.bulk_create([ ... Entry(headline='This is a test'), ... Entry(headline='This is only a test'), ... ])
注意事项:
pre_save
和post_save
信号。batch_size
参数控制在单个查询中创建的对象数,默认情况是一次数据库连接将所有创建动作完成,但这在要创建的对象数量和字段数量非常巨大的时候往往是不行的,比如SQLite3一次只允许最多999个变量。batch_size
参数的作用其实就类似文件的分块读写,参考下面的例子:
from itertools import islice batch_size = 100 objs = (Entry(headline='Test %s' % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
bulk_update(objs, fields, batch_size=None)
批量更新。
>>> objs = [ ... Entry.objects.create(headline='Entry 1'), ... Entry.objects.create(headline='Entry 2'), ... ] >>> objs[0].headline = 'This is entry 1' >>> objs[1].headline = 'This is entry 2' >>> Entry.objects.bulk_update(objs, ['headline'])
这种方式比for循环逐个更新效率要高,但是有一些使用限制:
save()
方法,也不会发送pre_save
和post_save
信号。count()
返回在数据库中对应的QuerySet对象的个数。count()永远不会引发异常。
例如:
# 返回总个数. Entry.objects.count() # 返回包含有'Lennon'的对象的总数 Entry.objects.filter(headline__contains='Lennon').count()
in_bulk(id_list=None, field_name='pk')
获取主键值的列表,并返回将每个主键值映射到具有给定ID的对象的实例的字典。 如果未提供列表,则会返回查询集中的所有对象。
例如:
>>> Blog.objects.in_bulk([1]) {1: <Blog: Beatles Blog>} >>> Blog.objects.in_bulk([1, 2]) {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>} >>> Blog.objects.in_bulk([]) {} >>> Blog.objects.in_bulk() {1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
如果向in_bulk()
传递一个空列表,会得到一个空的字典。
iterator(chunk_size=2000)
提交数据库操作,获取QuerySet,并返回一个迭代器。
QuerySet通常会在内部缓存其结果,以便在重复计算时不会导致额外的查询。而iterator()将直接读取结果,不在QuerySet级别执行任何缓存。对于返回大量只需要访问一次的对象的QuerySet,这可以带来更好的性能,显著减少内存使用。
请注意,在已经提交了的iterator()上使用QuerySet会强制它再次提交数据库操作,进行重复查询。此外,使用iterator()会导致先前的prefetch_related()
调用被忽略,因为这两个一起优化没有意义。
chunk_size
用于给数据请求分块,将大量的一次数据读写,分割成多次小量的读写。
返回QuerySet中最近的对象。
其实本质就是先排序,然后获取最前面那个对象。
下例根据Entry的'pub_date'字段返回最新发布的entry:
Entry.objects.latest('pub_date')
下面则使用两个不同的字段排序后,拿出第一个对象:
Entry.objects.latest('pub_date', '-expire_date')
earliest()和latest()可能会返回空日期的实例,可能需要过滤掉空值:
Entry.objects.filter(pub_date__isnull=False).latest('pub_date')
类同latest(),只不过是获取最早先的对象。
返回结果集的第一个对象, 当没有找到时返回None。如果QuerySet没有设置排序,则将会自动按主键进行排序。例如:
p = Article.objects.order_by('title', 'pub_date').first()
first()是一个简便方法,下面的例子和上面的代码效果是一样:
try: p = Article.objects.order_by('title', 'pub_date')[0] except IndexError: p = None
工作方式类似first(),只是返回的是查询集中最后一个对象。
aggregate(args, *kwargs)
聚合,进行数据统计,详见后面的章节。
例如,想知道所有Blog下的所有Entry 的数目:
>>> from django.db.models import Count >>> q = Blog.objects.aggregate(Count('entry')) {'entry__count': 16}
通过使用关键字参数来指定聚合函数,可以控制返回的聚合的值的名称:
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry')) {'number_of_entries': 16}
如果QuerySet包含任何结果,则返回True,否则返回False。
查找具有唯一性字段(例如primary_key)的模型是否在一个QuerySet中的最高效的方法是:
entry = Entry.objects.get(pk=123) if some_queryset.filter(pk=entry.pk).exists(): print("Entry contained in queryset")
它将比下面的方法快很多,这个方法要求对QuerySet求值并迭代整个QuerySet:
if entry in some_queryset: print("Entry contained in QuerySet")
若要查找一个QuerySet是否包含任何元素:
if some_queryset.exists(): print("There is at least one object in some_queryset")
将快于:
if some_queryset: print("There is at least one object in some_queryset")
对指定的字段执行更新操作,并返回匹配的行数(如果某些行已具有新值,则可能不等于已更新的行数)。
例如,要对2010年发布的所有博客条目关闭评论功能,可以执行以下操作:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
可以同时更新多个字段 (没有多少字段的限制)。 例如同时更新comments_on和headline字段:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
update()方法无需save操作,会立刻写入数据库。
update只能更新模型主表中的字段,不能更新关联模型中的字段。
例如不能这样做:
>>> Entry.objects.update(blog__name='foo') # 这样做是无效的
但是,仍然可以根据相关字段进行过滤:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
update()方法返回受影响的行数:
>>> Entry.objects.filter(id=64).update(comments_on=True) 1 >>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True) 0 >>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False) 132
如果你只是更新一下对象,不需要为对象做别的事情,最有效的方法是调用update(),而不是将模型对象加载到内存中。 例如,不要这样做:
e = Entry.objects.get(id=10) e.comments_on = False e.save()
建议如下操作:
Entry.objects.filter(id=10).update(comments_on=False)
用update()还可以防止在加载对象和调用save()之间的短时间内数据库中某些内容可能发生更改的竞争条件。
update()方法不会调用save()方法,也不会发出pre_save和post_save信号。
所以,如果想更新一个具有自定义save()方法的模型的记录,请循环遍历它们并调用save(),如下所示:
for e in Entry.objects.filter(pub_date__year=2020): e.comments_on = False e.save()
批量删除QuerySet中的所有对象,并返回删除的对象个数和每个对象类型的删除次数的字典。
delete()动作是立即执行的。
例如,要删除特定博客中的所有条目:
>>> b = Blog.objects.get(pk=1) # 删除b下所属的所有条目 >>> Entry.objects.filter(blog=b).delete() (4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})
默认情况下,任何具有指向要删除的对象的外键的对象将与它们一起被删除。 像这样:
>>> blogs = Blog.objects.all() # This will delete all Blogs and all of their Entry objects. >>> blogs.delete() (5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})
这种级联的行为可以通过的ForeignKey的on_delete
参数自定义。(什么时候要改变这种行为呢?比如日志数据,就不能和它关联的主体一并被删除!)
delete()会为所有已删除的对象(包括级联删除)发出pre_delete
和post_delete
信号。
classmethod as_manager()
一个类方法,返回Manager的实例与QuerySet的方法的副本。
explain(format=None, **options)
返回QuerySet的执行计划的字符串,该字符串详细说明数据库将如何执行查询,包括将要使用的任何索引或连接。了解这些细节可以帮助你提高查询的性能。
除了Oracle之外的内置数据库后端都支持explain。不同的数据库展示的信息不同。下面是个PostgreSQL的例子:
>>> print(Blog.objects.filter(title='My Blog').explain()) Seq Scan on blog (cost=0.00..35.50 rows=10 width=12) Filter: (title = 'My Blog'::bpchar)
对于PostgreSQL数据库,还支持一些特别的参数:
>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True)) Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1) Output: id, title Filter: (blog.title = 'My Blog'::bpchar) Planning time: 0.064 ms Execution time: 0.058 ms
使用format可以指定输出字符串的格式,根据数据库的支持程度的不同,可以是'TEXT'
, 'JSON'
, 'YAML'
或者'XML'
。
Django的ORM是模型的核心,绕不开,躲不掉,非常强大也非常复杂,不比SQLArchemy简单,既难学也难懂,知识点也多,需要常看常用,常记常试,没有捷径,唯有苦学。
注意,get()方法返回的结果不支持update(),要用filter()。
update() 是作用于查询集的,get()方法的到的是查询对象,当然不能用update了
其实常用的就那么多,多记一下就可以了,博主写的很赞
对get_or_create()和update_or_create()方法中的default参数解释不到位!!
朋友,你看着人家博主幸苦写的东西,不感谢也就罢了,有问题,委婉的提出来就好,需要这么不客气的评论人家吗?对原创和开源的知识分享要保有敬意,否则以后还有谁写博客,谁来分享?
说明看得仔细才能发现问题,对博主写出更好的文章有益。这也是对博主的敬意所在。
把问题指出来有什么不对吗?
会说话吗?不会好好说吗?
get_or_create方法可以有一个“defaults”参数,用来设置指定字段的默认值,
last()解释的字打错了