8000 Backport generic-relations data loss fix from [4428]. Thanks to Malco… · django/django@3fc2718 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3fc2718

Browse files
committed
Backport generic-relations data loss fix from [4428]. Thanks to Malcolm for pointing out my stupid error the first time I tried this.
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4476 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent b0ea212 commit 3fc2718

File tree

2 files changed

+49
-13
lines changed

2 files changed

+49
-13
lines changed

django/db/models/query.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.db import backend, connection, transaction
22
from django.db.models.fields import DateField, FieldDoesNotExist
3+
from django.db.models.fields.generic import GenericRelation
34
from django.db.models import signals
45
from django.dispatch import dispatcher
56
from django.utils.datastructures import SortedDict
@@ -925,18 +926,26 @@ def delete_objects(seen_objs):
925926

926927
pk_list = [pk for pk,instance in seen_objs[cls]]
927928
for related in cls._meta.get_all_related_many_to_many_objects():
928-
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
929-
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
930-
(qn(related.field.m2m_db_table()),
931-
qn(related.field.m2m_reverse_name()),
932-
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
933-
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
929+
if not isinstance(related.field, GenericRelation):
930+
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
931+
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
932+
(qn(related.field.m2m_db_table()),
933+
qn(related.field.m2m_reverse_name()),
934+
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
935+
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
934936
for f in cls._meta.many_to_many:
937+
if isinstance(f, GenericRelation):
938+
from django.contrib.contenttypes.models import ContentType
939+
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
940+
args_extra = [ContentType.objects.get_for_model(cls).id]
941+
else:
942+
query_extra = ''
943+
args_extra = []
935944
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
936945
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
937-
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
938-
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
939-
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
946+
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
947+
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])) + query_extra,
948+
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
940949
for field in cls._meta.fields:
941950
if field.rel and field.null and field.rel.to in seen_objs:
942951
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):

tests/modeltests/generic_relations/models.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ def __str__(self):
6565
6666
# Objects with declared GenericRelations can be tagged directly -- the API
6767
# mimics the many-to-many API.
68-
>>> lion.tags.create(tag="yellow")
69-
<TaggedItem: yellow>
70-
>>> lion.tags.create(tag="hairy")
71-
<TaggedItem: hairy>
7268
>>> bacon.tags.create(tag="fatty")
7369
<TaggedItem: fatty>
7470
>>> bacon.tags.create(tag="salty")
7571
<TaggedItem: salty>
72+
>>> lion.tags.create(tag="yellow")
73+
<TaggedItem: yellow>
74+
>>> lion.tags.create(tag="hairy")
75+
<TaggedItem: hairy>
7676
7777
>>> lion.tags.all()
7878
[<TaggedItem: hairy>, <TaggedItem: yellow>]
@@ -105,4 +105,31 @@ def __str__(self):
105105
[<TaggedItem: shiny>]
106106
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
107107
[<TaggedItem: clearish>]
108+
109+
110+
# If you delete an object with an explicit Generic relation, the related
111+
# objects are deleted when the source object is deleted.
112+
# Original list of tags:
113+
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
114+
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
115+
116+
>>> lion.delete()
117+
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
118+
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
119+
120+
# If Generic Relation is not explicitly defined, any related objects
121+
# remain after deletion of the source object.
122+
>>> quartz.delete()
123+
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
124+
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
125+
126+
# If you delete a tag, the objects using the tag are unaffected
127+
# (other than losing a tag)
128+
>>> tag = TaggedItem.objects.get(id=1)
129+
>>> tag.delete()
130+
>>> bacon.tags.all()
131+
[<TaggedItem: salty>]
132+
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
133+
[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
134+
108135
"""

0 commit comments

Comments
 (0)
0