get_or_create函数

get_or_create(defaults=None, **kwargs)

一个通过给出的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(1940, 10, 9))
obj.save()
如果模型的字段数量较大的话,这种模式就变的非常不易用了。
上面的示例可以用get_or_create()重写 :

obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)})

任何传递给 get_or_create() 的关键字参数,除了一个可选的defaults,都将传递给get() 调用。
如果查找到一个对象,get_or_create() 返回一个包含匹配到的对象以及False 组成的元组。
如果查找到的对象超过一个以上,get_or_create 将引发MultipleObjectsReturned。
如果查找不到对象, get_or_create() 将会实例化并保存一个新的对象,
返回一个由新的对象以及True 组成的元组。新的对象将会大概按照以下的逻辑创建:

params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update(defaults)
obj = self.model(**params)

它表示从非’defaults’ 且不包含双下划线的关键字参数开始(暗示这是一个不精确的查询)。
然后将defaults 的内容添加进来,覆盖必要的键,并使用结果作为关键字参数传递给模型类。
这是对用到的算法的简单描述,但它包含了所有的相关的细节。
内部的实现有更多的错误检查并处理一些边缘条件;如果感兴趣,请阅读代码。

如果你有一个名为defaults的字段,并且想在get_or_create() 是用它作为精确查询,只需要使用’defaults__exact’,像这样:

Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})

当你使用手动指定的主键时,get_or_create() 方法与create()方法有相似的错误行为 。如果需要创建一个对象而该对象的主键早已存在于数据库中,IntegrityError 异常将会被触发。

这个方法假设正确使用原子操作,正确的数据库配置和底层数据库的正确行为。然而,如果数据库级别没有对get_or_create 中用到的kwargs 强制要求唯一性(参见unique 和 unique_together),这个方法容易导致竞态条件可能会仍具有相同参数的多行同时插入。

如果你正在使用MySQL,请确保使用READ COMMITTED 隔离级别而不是默认的REPEATABLE READ,否则你将会遇到get_or_create 引发IntegrityError 但对象在接下来的get() 调用中并不存在的情况。

最后讲一句get_or_create() 在Django 视图中的使用。请确保只在POST 请求中使用,除非你有充分的理由。GET 请求不应该对数据有任何影响。而POST 则用于对数据产生影响的请求。更多信息,参见HTTP 细则中的安全的方法。

 

警告

你可以通过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”,
但是它不能:关联关系不能获取这个chapter 因为它与这个book 不关联,
但因为title 字段是唯一的它仍然不能创建。


本文出自:https://zhuanlan.zhihu.com/p/34856119


posted @ 2018-09-30 17:18  Aray007  阅读(7275)  评论(0编辑  收藏  举报