diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index bd2e288..fc2f6d2 100644
a
|
b
|
class ForeignRelatedObjectsDescriptor(object):
|
542 | 542 | if rel_field.null: |
543 | 543 | def remove(self, *objs): |
544 | 544 | val = getattr(self.instance, attname) |
| 545 | ids = [] |
545 | 546 | for obj in objs: |
546 | 547 | # Is obj actually part of this descriptor set? |
547 | 548 | if getattr(obj, rel_field.attname) == val: |
548 | | setattr(obj, rel_field.name, None) |
549 | | obj.save() |
| 549 | ids.append(obj.pk) |
550 | 550 | else: |
551 | 551 | raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance)) |
| 552 | self.filter(pk__in=ids).update(**{rel_field.name: None}) |
552 | 553 | remove.alters_data = True |
553 | 554 | |
554 | 555 | def clear(self): |
diff --git a/docs/ref/models/relations.txt b/docs/ref/models/relations.txt
index 37986ec..fa1ed38 100644
a
|
b
|
Related objects reference
|
91 | 91 | :class:`~django.db.models.ForeignKey` doesn't have ``null=True``, this |
92 | 92 | is invalid. |
93 | 93 | |
| 94 | .. versionchanged:: 1.6 |
| 95 | |
| 96 | The ``remove()`` method executes one query regardless of the number of |
| 97 | objects being removed. In prior versions, it executed one query per |
| 98 | object by calling the ``save()`` method of each object being removed. |
| 99 | |
94 | 100 | .. method:: clear() |
95 | 101 | |
96 | 102 | Removes all objects from the related object set:: |
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index 5d61517..cb8979b 100644
a
|
b
|
Backwards incompatible changes in 1.6
|
108 | 108 | deprecation timeline for a given feature, its removal may appear as a |
109 | 109 | backwards incompatible change. |
110 | 110 | |
| 111 | * The :meth:`~django.db.models.fields.related.RelatedManager.remove` method on |
| 112 | a reverse foreign key now does one query regardless of the number of objects |
| 113 | removed rather than one query per object. The ``save()`` method of the |
| 114 | objects being removed is no longer called and thus |
| 115 | :data:`~django.db.models.signals.pre_save` and |
| 116 | :data:`~django.db.models.signals.post_save` signals are no longer sent. This |
| 117 | is consistent with how |
| 118 | :meth:`~django.db.models.fields.related.RelatedManager.clear` works. |
| 119 | |
111 | 120 | Features deprecated in 1.6 |
112 | 121 | ========================== |
113 | 122 | |
diff --git a/tests/modeltests/many_to_one_null/tests.py b/tests/modeltests/many_to_one_null/tests.py
index 4de44b5..935733c 100644
a
|
b
|
class ManyToOneNullTests(TestCase):
|
93 | 93 | with self.assertNumQueries(1): |
94 | 94 | r.article_set.clear() |
95 | 95 | self.assertEqual(r.article_set.count(), 0) |
| 96 | |
| 97 | def test_remove_efficiency(self): |
| 98 | r = Reporter.objects.create() |
| 99 | articles = [] |
| 100 | for _ in xrange(3): |
| 101 | articles.append(r.article_set.create()) |
| 102 | with self.assertNumQueries(1): |
| 103 | r.article_set.remove(*articles) |
| 104 | self.assertEqual(r.article_set.count(), 0) |