以前不知道这个函数,结果自己编过一个类似的函数,实现这个功能:
# 查子表中是否有要插入的项,比如基站类型中是否已经有宏站这一项,如果有就返回这一项的对象,没有就添加宏站数据并返回对象 # 返回值时找到的一条数据对象,或者创建的一条数据对象
def find_or_create_field_content_in_some_class(classmodel, criteria):
query_set = classmodel.objects.filter(**criteria)
# 逻辑搞错了,当len(query_set)=0时,if语句不会执行,也就是说当没有查询到相应的数据库记录时,不执行if内代码
# 正着看,就是当len(query_set)=1时,执行if语句,即查询到数据库结果时,应该执行返回查询结果的代码。
# 让我错搞成0 == len(query_set)时,应该执行if,创建数据库中没有的记录。
if query_set.exists():
return query_set.first()
return classmodel.objects.create(**criteria)
现在可以用原版的函数替换它了。
get_or_create()
¶
get_or_create
(defaults=None, **kwargs)¶
A convenience method for looking up an object with the given kwargs
(may be empty if your model has defaults for all fields), creating one if necessary.
Returns a tuple of (object, created)
, where object
is the retrieved or created object and created
is a boolean specifying whether a new object was created.
This is meant as a shortcut to boilerplatish code. For example:
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()
This pattern gets quite unwieldy as the number of fields in a model goes up. The above example can be rewritten using get_or_create()
like so:
obj, created = Person.objects.get_or_create( first_name='John', last_name='Lennon', defaults={'birthday': date(1940, 10, 9)}, )
Any keyword arguments passed to get_or_create()
— except an optional one called defaults
— will be used in a get()
call. If an object is found, get_or_create()
returns a tuple of that object and False
. If multiple objects are found, get_or_create
raises MultipleObjectsReturned
. If an object is not found, get_or_create()
will instantiate and save a new object, returning a tuple of the new object and True
. The new object will be created roughly according to this algorithm:
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()
In English, that means start with any non-'defaults'
keyword argument that doesn’t contain a double underscore (which would indicate a non-exact lookup). Then add the contents of defaults
, overriding any keys if necessary, and use the result as the keyword arguments to the model class. If there are any callables in defaults
, evaluate them. As hinted at above, this is a simplification of the algorithm that is used, but it contains all the pertinent details. The internal implementation has some more error-checking than this and handles some extra edge-conditions; if you’re interested, read the code.
If you have a field named defaults
and want to use it as an exact lookup in get_or_create()
, just use 'defaults__exact'
, like so:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
The get_or_create()
method has similar error behavior to create()
when you’re using manually specified primary keys. If an object needs to be created and the key already exists in the database, an IntegrityError
will be raised.
This method is atomic assuming correct usage, correct database configuration, and correct behavior of the underlying database. However, if uniqueness is not enforced at the database level for the kwargs
used in a get_or_create
call (see unique
or unique_together
), this method is prone to a race-condition which can result in multiple rows with the same parameters being inserted simultaneously.
If you are using MySQL, be sure to use the READ COMMITTED
isolation level rather than REPEATABLE READ
(the default), otherwise you may see cases where get_or_create
will raise an IntegrityError
but the object won’t appear in a subsequent get()
call.
Finally, a word on using get_or_create()
in Django views. Please make sure to use it only in POST
requests unless you have a good reason not to. GET
requests shouldn’t have any effect on data. Instead, use POST
whenever a request to a page has a side effect on your data. For more, see Safe methods in the HTTP spec.
Warning
You can use get_or_create()
through ManyToManyField
attributes and reverse relations. In that case you will restrict the queries inside the context of that relation. That could lead you to some integrity problems if you don’t use it consistently.
Being the following models:
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)
You can use get_or_create()
through Book’s chapters field, but it only fetches inside the context of that 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
This is happening because it’s trying to get or create “Chapter 1” through the book “Ulysses”, but it can’t do any of them: the relation can’t fetch that chapter because it isn’t related to that book, but it can’t create it either because title
field should be unique.
Changed in Django 1.11:
Added support for callable values in defaults
.