Code

Ticket #16649: save_inherit.diff

File save_inherit.diff, 3.1 KB (added by akaariai, 3 years ago)

Optimization for inherited model insertion, now the correct version

Line 
1diff --git a/django/db/models/base.py b/django/db/models/base.py
2index 2e6bea8..db25494 100644
3--- a/django/db/models/base.py
4+++ b/django/db/models/base.py
5@@ -24,6 +24,7 @@ from django.utils.functional import curry
6 from django.utils.encoding import smart_str, force_unicode
7 from django.utils.text import get_text_list, capfirst
8 
9+UPDATED, INSERTED = 'UPDATED', 'INSERTED'
10 
11 class ModelBase(type):
12     """
13@@ -473,6 +474,11 @@ class Model(object):
14         override this method. It's separate from save() in order to hide the
15         need for overrides of save() to pass around internal-only parameters
16         ('raw', 'cls', and 'origin').
17+
18+        Returns the action taken, which can be UPDATED or INSERTED. This is the
19+        action taken for the current model. It is possible that parent model
20+        was updated while current model is inserted. If the return value is
21+        UPDATED, then every model in the base chain was updated.
22         """
23         using = using or router.db_for_write(self.__class__, instance=self)
24         connection = connections[using]
25@@ -494,6 +500,7 @@ class Model(object):
26         # attributes we have been given to the class we have been given.
27         # We also go through this process to defer the save of proxy objects
28         # to their actual underlying model.
29+        parent_action = None
30         if not raw or meta.proxy:
31             if meta.proxy:
32                 org = cls
33@@ -506,12 +513,15 @@ class Model(object):
34                 if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
35                     setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
36 
37-                self.save_base(cls=parent, origin=org, using=using)
38+                parent_action = self.save_base(cls=parent, origin=org, using=using)
39 
40                 if field:
41                     setattr(self, field.attname, self._get_pk_val(parent._meta))
42             if meta.proxy:
43-                return
44+                return parent_action
45+        # A small optimization: if parent was inserted, we know this model must be inserted, too.
46+        if parent_action == INSERTED:
47+            force_insert = True
48 
49         if not meta.proxy:
50             non_pks = [f for f in meta.local_fields if not f.primary_key]
51@@ -546,7 +556,7 @@ class Model(object):
52                         
53                 elif not non_pks:
54                     # There are no other fields than the pk in the model. In this case we do
55-                    # not check the existense by update
56+                    # not check the existense by update.
57                     if manager.using(using).filter(pk=pk_val).exists():
58                         record_exists = True
59             if not pk_set or not record_exists:
60@@ -590,6 +600,12 @@ class Model(object):
61             signals.post_save.send(sender=origin, instance=self,
62                 created=(not record_exists), raw=raw, using=using)
63 
64+        if meta.proxy:
65+            return parent_action
66+        if record_exists:
67+            return UPDATED
68+        else:
69+            return INSERTED
70 
71     save_base.alters_data = True
72