Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #16865 -- Made get_or_create use read database for initial get …

…query.

Thanks Rick van Hattem for the report and trbs for the patch.
  • Loading branch information...
commit 901af865505310a70dd02ea5b3becbf45819b652 1 parent 44767f2
@carljm carljm authored
View
2  django/db/models/query.py
@@ -455,9 +455,9 @@ def get_or_create(self, **kwargs):
if f.attname in lookup:
lookup[f.name] = lookup.pop(f.attname)
try:
- self._for_write = True
return self.get(**lookup), False
except self.model.DoesNotExist:
+ self._for_write = True
try:
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
params.update(defaults)
View
9 docs/releases/1.5.txt
@@ -338,6 +338,15 @@ Miscellaneous
needs. The new default value is `0666` (octal) and the current umask value
is first masked out.
+* In a multi-database situation, ``get_or_create()`` will now use a read
+ database for the initial ``get`` attempt (previously, it used only the write
+ database for all queries). This change reduces load on the write (master)
+ database, in exchange for slightly more frequent false-negatives on the
+ initial ``get`` due to replication lag. In those cases the subsequent insert
+ will still go to the master and fail, after which the existing object will be
+ fetched from the master.
+
+
Features deprecated in 1.5
==========================
View
24 tests/modeltests/get_or_create/tests.py
@@ -64,3 +64,27 @@ def test_get_or_create(self):
formatted_traceback = traceback.format_exc()
self.assertIn('obj.save', formatted_traceback)
+
+ def test_initial_get_on_read_db(self):
+ """
+ get_or_create should only set _for_write when it's actually doing a
+ create action. This makes sure that the initial .get() will be able to
+ use a slave database. Specially when some form of database pinning is
+ in place this will help to not put all the SELECT queries on the
+ master. Refs #16865.
+
+ """
+ qs = Person.objects.get_query_set()
+ p, created = qs.get_or_create(
+ first_name="Stuart", last_name="Sutcliffe", defaults={
+ "birthday": date(1940, 6, 23),
+ }
+ )
+ self.assertTrue(created)
+ self.assertTrue(qs._for_write)
+
+ qs = Person.objects.get_query_set()
+ p, created = qs.get_or_create(
+ first_name="Stuart", last_name="Sutcliffe")
+ self.assertFalse(created)
+ self.assertFalse(qs._for_write)
Please sign in to comment.
Something went wrong with that request. Please try again.