Author: russellm
Date: 2007-01-25 05:24:17 -0600 (Thu, 25 Jan 2007)
New Revision: 4428

Modified:
   django/trunk/django/db/models/query.py
   django/trunk/tests/modeltests/generic_relations/models.py
Log:
Fixed #3215, #3081, #2749 -- Fixed problem with mistaken deletion of objects 
when a GenericRelation is involved. Thanks to Thomas Steinacher for helping to 
narrow down the problem (#3215), and Alex Dedul (#3081) for the starting point 
of a working patch.


Modified: django/trunk/django/db/models/query.py
===================================================================
--- django/trunk/django/db/models/query.py      2007-01-25 04:20:49 UTC (rev 
4427)
+++ django/trunk/django/db/models/query.py      2007-01-25 11:24:17 UTC (rev 
4428)
@@ -1,5 +1,6 @@
 from django.db import backend, connection, transaction
 from django.db.models.fields import DateField, FieldDoesNotExist
+from django.db.models.fields.generic import GenericRelation
 from django.db.models import signals
 from django.dispatch import dispatcher
 from django.utils.datastructures import SortedDict
@@ -979,18 +980,26 @@
 
         pk_list = [pk for pk,instance in seen_objs[cls]]
         for related in cls._meta.get_all_related_many_to_many_objects():
-            for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
-                cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
-                    (qn(related.field.m2m_db_table()),
-                        qn(related.field.m2m_reverse_name()),
-                        ','.join(['%s' for pk in 
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
-                    pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
+            if not isinstance(related.field, GenericRelation):
+                for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
+                    cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
+                        (qn(related.field.m2m_db_table()),
+                            qn(related.field.m2m_reverse_name()),
+                            ','.join(['%s' for pk in 
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
+                        pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
         for f in cls._meta.many_to_many:
+            if isinstance(f, GenericRelation):
+                from django.contrib.contenttypes.models import ContentType 
+                query_extra = 'AND %s=%%s' % 
f.rel.to._meta.get_field(f.content_type_field_name).column
+                args_extra = [ContentType.objects.get_for_model(cls).id]
+            else:
+                query_extra = ''
+                args_extra = []
             for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
-                cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
+                cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \
                     (qn(f.m2m_db_table()), qn(f.m2m_column_name()),
-                    ','.join(['%s' for pk in 
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
-                    pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
+                    ','.join(['%s' for pk in 
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra,
+                    pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + 
args_extra)
         for field in cls._meta.fields:
             if field.rel and field.null and field.rel.to in seen_objs:
                 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):

Modified: django/trunk/tests/modeltests/generic_relations/models.py
===================================================================
--- django/trunk/tests/modeltests/generic_relations/models.py   2007-01-25 
04:20:49 UTC (rev 4427)
+++ django/trunk/tests/modeltests/generic_relations/models.py   2007-01-25 
11:24:17 UTC (rev 4428)
@@ -65,14 +65,14 @@
 
 # Objects with declared GenericRelations can be tagged directly -- the API
 # mimics the many-to-many API.
+>>> bacon.tags.create(tag="fatty")
+<TaggedItem: fatty>
+>>> bacon.tags.create(tag="salty")
+<TaggedItem: salty>
 >>> lion.tags.create(tag="yellow")
 <TaggedItem: yellow>
 >>> lion.tags.create(tag="hairy")
 <TaggedItem: hairy>
->>> bacon.tags.create(tag="fatty")
-<TaggedItem: fatty>
->>> bacon.tags.create(tag="salty")
-<TaggedItem: salty>
 
 >>> lion.tags.all()
 [<TaggedItem: hairy>, <TaggedItem: yellow>]
@@ -105,4 +105,30 @@
 [<TaggedItem: shiny>]
 >>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
 [<TaggedItem: clearish>]
+
+# If you delete an object with an explicit Generic relation, the related
+# objects are deleted when the source object is deleted.
+# Original list of tags:
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 
2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 
2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
+
+>>> lion.delete()
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 
2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+
+# If Generic Relation is not explicitly defined, any related objects 
+# remain after deletion of the source object.
+>>> quartz.delete()
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 
2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+
+# If you delete a tag, the objects using the tag are unaffected 
+# (other than losing a tag)
+>>> tag = TaggedItem.objects.get(id=1)
+>>> tag.delete()
+>>> bacon.tags.all()
+[<TaggedItem: salty>]
+>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
+[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 
2), ('shiny', <ContentType: animal>, 2)]
+
 """}


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to