Django

Code

Changeset 4428

Show
Ignore:
Timestamp:
01/25/07 05:24:17 (2 years ago)
Author:
russellm
Message:

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.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/query.py

    r4394 r4428  
    11from django.db import backend, connection, transaction 
    22from django.db.models.fields import DateField, FieldDoesNotExist 
     3from django.db.models.fields.generic import GenericRelation 
    34from django.db.models import signals 
    45from django.dispatch import dispatcher 
     
    980981        pk_list = [pk for pk,instance in seen_objs[cls]] 
    981982        for related in cls._meta.get_all_related_many_to_many_objects(): 
     983            if not isinstance(related.field, GenericRelation): 
     984                for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
     985                    cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 
     986                        (qn(related.field.m2m_db_table()), 
     987                            qn(related.field.m2m_reverse_name()), 
     988                            ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), 
     989                        pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) 
     990        for f in cls._meta.many_to_many: 
     991            if isinstance(f, GenericRelation): 
     992                from django.contrib.contenttypes.models import ContentType  
     993                query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column 
     994                args_extra = [ContentType.objects.get_for_model(cls).id] 
     995            else: 
     996                query_extra = '' 
     997                args_extra = [] 
    982998            for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    983                 cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 
    984                     (qn(related.field.m2m_db_table()), 
    985                         qn(related.field.m2m_reverse_name()), 
    986                         ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), 
    987                     pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) 
    988         for f in cls._meta.many_to_many: 
    989             for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    990                 cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 
     999                cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \ 
    9911000                    (qn(f.m2m_db_table()), qn(f.m2m_column_name()), 
    992                     ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))
    993                     pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]
     1001                    ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra
     1002                    pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra
    9941003        for field in cls._meta.fields: 
    9951004            if field.rel and field.null and field.rel.to in seen_objs: 
  • django/trunk/tests/modeltests/generic_relations/models.py

    r3661 r4428  
    6666# Objects with declared GenericRelations can be tagged directly -- the API 
    6767# mimics the many-to-many API. 
     68>>> bacon.tags.create(tag="fatty") 
     69<TaggedItem: fatty> 
     70>>> bacon.tags.create(tag="salty") 
     71<TaggedItem: salty> 
    6872>>> lion.tags.create(tag="yellow") 
    6973<TaggedItem: yellow> 
    7074>>> lion.tags.create(tag="hairy") 
    7175<TaggedItem: hairy> 
    72 >>> bacon.tags.create(tag="fatty") 
    73 <TaggedItem: fatty> 
    74 >>> bacon.tags.create(tag="salty") 
    75 <TaggedItem: salty> 
    7676 
    7777>>> lion.tags.all() 
     
    106106>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id) 
    107107[<TaggedItem: clearish>] 
     108 
     109# If you delete an object with an explicit Generic relation, the related 
     110# objects are deleted when the source object is deleted. 
     111# Original list of tags: 
     112>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     113[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)] 
     114 
     115>>> lion.delete() 
     116>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     117[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)] 
     118 
     119# If Generic Relation is not explicitly defined, any related objects  
     120# remain after deletion of the source object. 
     121>>> quartz.delete() 
     122>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     123[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)] 
     124 
     125# If you delete a tag, the objects using the tag are unaffected  
     126# (other than losing a tag) 
     127>>> tag = TaggedItem.objects.get(id=1) 
     128>>> tag.delete() 
     129>>> bacon.tags.all() 
     130[<TaggedItem: salty>] 
     131>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     132[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)] 
     133 
    108134"""}