소스 검색

Fixed #30199 -- Adjusted QuerySet.get_or_create() docs to highlight atomicity warning.

Alex 5 년 전
부모
커밋
1686dce06c
3개의 변경된 파일19개의 추가작업 그리고 18개의 파일을 삭제
  1. 1 0
      AUTHORS
  2. 4 1
      docs/ref/databases.txt
  3. 14 17
      docs/ref/models/querysets.txt

+ 1 - 0
AUTHORS

@@ -38,6 +38,7 @@ answer newbie questions, and generally made Django that much better:
     Alexander Dutton <dev@alexdutton.co.uk>
     Alexander Myodov <alex@myodov.com>
     Alexandr Tatarinov <tatarinov1997@gmail.com>
+    Alex Becker <https://alexcbecker.net/>
     Alex Couper <http://alexcouper.com/>
     Alex Dedul
     Alex Gaynor <alex.gaynor@gmail.com>

+ 4 - 1
docs/ref/databases.txt

@@ -488,7 +488,10 @@ this entry are the four standard isolation levels:
 
 or ``None`` to use the server's configured isolation level. However, Django
 works best with and defaults to read committed rather than MySQL's default,
-repeatable read. Data loss is possible with repeatable read.
+repeatable read. Data loss is possible with repeatable read. In particular,
+you may see cases where :meth:`~django.db.models.query.QuerySet.get_or_create`
+will raise an :exc:`~django.db.IntegrityError` but the object won't appear in
+a subsequent :meth:`~django.db.models.query.QuerySet.get` call.
 
 .. _transaction isolation level: https://dev.mysql.com/doc/refman/en/innodb-transaction-isolation-levels.html
 

+ 14 - 17
docs/ref/models/querysets.txt

@@ -1879,7 +1879,8 @@ 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::
+This is meant to prevent duplicate objects from being created when requests are
+made in parallel, and as a shortcut to boilerplatish code. For example::
 
     try:
         obj = Person.objects.get(first_name='John', last_name='Lennon')
@@ -1887,8 +1888,9 @@ This is meant as a shortcut to boilerplatish code. For example::
         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::
+Here, with concurrent requests, multiple attempts to save a ``Person`` with
+the same parameters may be made. To avoid this race condition, the above
+example can be rewritten using ``get_or_create()`` like so::
 
     obj, created = Person.objects.get_or_create(
         first_name='John',
@@ -1900,6 +1902,15 @@ Any keyword arguments passed to ``get_or_create()`` — *except* an optional one
 called ``defaults`` — will be used in a :meth:`get()` call. If an object is
 found, ``get_or_create()`` returns a tuple of that object and ``False``.
 
+.. warning::
+
+    This method is atomic assuming that the database enforces uniqueness of the
+    keyword arguments (see :attr:`~django.db.models.Field.unique` or
+    :attr:`~django.db.models.Options.unique_together`). If the fields used in the
+    keyword arguments do not have a uniqueness constraint, concurrent calls to
+    this method may result in multiple rows with the same parameters being
+    inserted.
+
 You can specify more complex conditions for the retrieved object by chaining
 ``get_or_create()`` with ``filter()`` and using :class:`Q objects
 <django.db.models.Q>`. For example, to retrieve Robert or Bob Marley if either
@@ -1941,20 +1952,6 @@ when you're using manually specified primary keys. If an object needs to be
 created and the key already exists in the database, an
 :exc:`~django.db.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 :attr:`~django.db.models.Field.unique` or
-:attr:`~django.db.models.Options.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 :exc:`~django.db.IntegrityError` but the
-object won't appear in a subsequent :meth:`~django.db.models.query.QuerySet.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``