Code

Ticket #3215: 3215.patch

File 3215.patch, 3.4 KB (added by russellm, 7 years ago)

Full patch (including tests) for deleting generic related objects

  • django/db/models/query.py

     
    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 
    56from django.utils.datastructures import SortedDict 
     
    986987                        ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])), 
    987988                    pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) 
    988989        for f in cls._meta.many_to_many: 
     990            if isinstance(f, GenericRelation): 
     991                from django.contrib.contenttypes.models import ContentType  
     992                query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column 
     993                args_extra = [ContentType.objects.get_for_model(cls).id] 
     994            else: 
     995                query_extra = '' 
     996                args_extra = [] 
    989997            for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    990                 cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \ 
     998                cursor.execute(("DELETE FROM %s WHERE %s IN (%s)" % \ 
    991999                    (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]) 
     1000                    ','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]]))) + query_extra, 
     1001                    pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra) 
    9941002        for field in cls._meta.fields: 
    9951003            if field.rel and field.null and field.rel.to in seen_objs: 
    9961004                for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
  • tests/modeltests/generic_relations/models.py

     
    105105[<TaggedItem: shiny>] 
    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>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     112[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)] 
     113 
     114>>> lion.delete() 
     115>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     116[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)] 
     117 
     118# If Generic Relation is not explicitly defined, any related objects  
     119# remain after deletion of the source object. 
     120>>> quartz.delete() 
     121>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()] 
     122[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)] 
     123 
    108124"""}