diff options
| author | Tim Graham <timograham@gmail.com> | 2015-03-14 21:25:33 -0400 |
|---|---|---|
| committer | Loïc Bistuer <loic.bistuer@gmail.com> | 2015-07-28 09:28:25 +0700 |
| commit | adc0c4fbac98f9cb975e8fa8220323b2de638b46 (patch) | |
| tree | 6d00b444423b09a764fa3eb7d5b9f278c6531c0e /django | |
| parent | c2e70f02653519db3a49cd48f5158ccad7434d25 (diff) | |
Fixed #18556 -- Allowed RelatedManager.add() to execute 1 query where possible.
Thanks Loic Bistuer for review.
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/contenttypes/fields.py | 42 | ||||
| -rw-r--r-- | django/db/models/fields/related.py | 43 |
2 files changed, 65 insertions, 20 deletions
diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 6548c39b4b..0980ed21d1 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -501,15 +501,38 @@ def create_generic_related_manager(superclass, rel): False, self.prefetch_cache_name) - def add(self, *objs): + def add(self, *objs, **kwargs): + bulk = kwargs.pop('bulk', True) db = router.db_for_write(self.model, instance=self.instance) - with transaction.atomic(using=db, savepoint=False): + + def check_and_update_obj(obj): + if not isinstance(obj, self.model): + raise TypeError("'%s' instance expected, got %r" % ( + self.model._meta.object_name, obj + )) + setattr(obj, self.content_type_field_name, self.content_type) + setattr(obj, self.object_id_field_name, self.pk_val) + + if bulk: + pks = [] for obj in objs: - if not isinstance(obj, self.model): - raise TypeError("'%s' instance expected" % self.model._meta.object_name) - setattr(obj, self.content_type_field_name, self.content_type) - setattr(obj, self.object_id_field_name, self.pk_val) - obj.save() + if obj._state.adding or obj._state.db != db: + raise ValueError( + "%r instance isn't saved. Use bulk=False or save " + "the object first. but must be." % obj + ) + check_and_update_obj(obj) + pks.append(obj.pk) + + self.model._base_manager.using(db).filter(pk__in=pks).update(**{ + self.content_type_field_name: self.content_type, + self.object_id_field_name: self.pk_val, + }) + else: + with transaction.atomic(using=db, savepoint=False): + for obj in objs: + check_and_update_obj(obj) + obj.save() add.alters_data = True def remove(self, *objs, **kwargs): @@ -542,13 +565,14 @@ def create_generic_related_manager(superclass, rel): # could be affected by `manager.clear()`. Refs #19816. objs = tuple(objs) + bulk = kwargs.pop('bulk', True) clear = kwargs.pop('clear', False) db = router.db_for_write(self.model, instance=self.instance) with transaction.atomic(using=db, savepoint=False): if clear: self.clear() - self.add(*objs) + self.add(*objs, bulk=bulk) else: old_objs = set(self.using(db).all()) new_objs = [] @@ -559,7 +583,7 @@ def create_generic_related_manager(superclass, rel): new_objs.append(obj) self.remove(*old_objs) - self.add(*new_objs) + self.add(*new_objs, bulk=bulk) set.alters_data = True def create(self, **kwargs): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index c10b2658b5..77b7bcb8d3 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -765,16 +765,36 @@ def create_foreign_related_manager(superclass, rel): cache_name = self.field.related_query_name() return queryset, rel_obj_attr, instance_attr, False, cache_name - def add(self, *objs): + def add(self, *objs, **kwargs): + bulk = kwargs.pop('bulk', True) objs = list(objs) db = router.db_for_write(self.model, instance=self.instance) - with transaction.atomic(using=db, savepoint=False): + + def check_and_update_obj(obj): + if not isinstance(obj, self.model): + raise TypeError("'%s' instance expected, got %r" % ( + self.model._meta.object_name, obj, + )) + setattr(obj, self.field.name, self.instance) + + if bulk: + pks = [] for obj in objs: - if not isinstance(obj, self.model): - raise TypeError("'%s' instance expected, got %r" % - (self.model._meta.object_name, obj)) - setattr(obj, self.field.name, self.instance) - obj.save() + check_and_update_obj(obj) + if obj._state.adding or obj._state.db != db: + raise ValueError( + "%r instance isn't saved. Use bulk=False or save " + "the object first." % obj + ) + pks.append(obj.pk) + self.model._base_manager.using(db).filter(pk__in=pks).update(**{ + self.field.name: self.instance, + }) + else: + with transaction.atomic(using=db, savepoint=False): + for obj in objs: + check_and_update_obj(obj) + obj.save() add.alters_data = True def create(self, **kwargs): @@ -835,6 +855,7 @@ def create_foreign_related_manager(superclass, rel): # could be affected by `manager.clear()`. Refs #19816. objs = tuple(objs) + bulk = kwargs.pop('bulk', True) clear = kwargs.pop('clear', False) if self.field.null: @@ -842,7 +863,7 @@ def create_foreign_related_manager(superclass, rel): with transaction.atomic(using=db, savepoint=False): if clear: self.clear() - self.add(*objs) + self.add(*objs, bulk=bulk) else: old_objs = set(self.using(db).all()) new_objs = [] @@ -852,10 +873,10 @@ def create_foreign_related_manager(superclass, rel): else: new_objs.append(obj) - self.remove(*old_objs) - self.add(*new_objs) + self.remove(*old_objs, bulk=bulk) + self.add(*new_objs, bulk=bulk) else: - self.add(*objs) + self.add(*objs, bulk=bulk) set.alters_data = True return RelatedManager |
