Commit 901af865 authored by Carl Meyer's avatar Carl Meyer
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.
parent 44767f2c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -455,9 +455,9 @@ class QuerySet(object):
            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)
+9 −0
Original line number Diff line number Diff line
@@ -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
==========================

+24 −0
Original line number Diff line number Diff line
@@ -64,3 +64,27 @@ class GetOrCreateTests(TestCase):
            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)