mirror of
				https://github.com/django/django.git
				synced 2025-10-29 00:26:07 +00:00 
			
		
		
		
	[1.6.x] Fixed #22998 -- Updated the fast_delete logic for GFKs
Backport of 6e2b82fdf6 from master
			
			
This commit is contained in:
		
				
					committed by
					
						 Anssi Kääriäinen
						Anssi Kääriäinen
					
				
			
			
				
	
			
			
			
						parent
						
							609153d2bf
						
					
				
				
					commit
					227a0f27a6
				
			| @@ -136,9 +136,9 @@ class Collector(object): | |||||||
|             include_hidden=True, include_proxy_eq=True): |             include_hidden=True, include_proxy_eq=True): | ||||||
|             if related.field.rel.on_delete is not DO_NOTHING: |             if related.field.rel.on_delete is not DO_NOTHING: | ||||||
|                 return False |                 return False | ||||||
|         # GFK deletes |         for field in model._meta.virtual_fields: | ||||||
|         for relation in opts.many_to_many: |             if hasattr(field, 'bulk_related_objects'): | ||||||
|             if not relation.rel.through: |                 # It's something like generic foreign key. | ||||||
|                 return False |                 return False | ||||||
|         return True |         return True | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ from django.contrib.contenttypes import generic | |||||||
| from django.contrib.contenttypes.models import ContentType | from django.contrib.contenttypes.models import ContentType | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.utils.encoding import python_2_unicode_compatible | from django.utils.encoding import python_2_unicode_compatible | ||||||
|  | from django.db.models.deletion import ProtectedError | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', | __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', | ||||||
| @@ -164,3 +165,26 @@ class D(models.Model): | |||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         ordering = ('id',) |         ordering = ('id',) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Ticket #22998 | ||||||
|  |  | ||||||
|  | class Node(models.Model): | ||||||
|  |     content_type = models.ForeignKey(ContentType) | ||||||
|  |     object_id = models.PositiveIntegerField() | ||||||
|  |     content = generic.GenericForeignKey('content_type', 'object_id') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Content(models.Model): | ||||||
|  |     nodes = generic.GenericRelation(Node) | ||||||
|  |     related_obj = models.ForeignKey('Related', on_delete=models.CASCADE) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Related(models.Model): | ||||||
|  |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def prevent_deletes(sender, instance, **kwargs): | ||||||
|  |     raise ProtectedError("Not allowed to delete.", [instance]) | ||||||
|  |  | ||||||
|  | models.signals.pre_delete.connect(prevent_deletes, sender=Node) | ||||||
|   | |||||||
| @@ -2,11 +2,14 @@ from django.db.models import Q | |||||||
| from django.db.utils import IntegrityError | from django.db.utils import IntegrityError | ||||||
| from django.test import TestCase, skipIfDBFeature | from django.test import TestCase, skipIfDBFeature | ||||||
| from django.forms.models import modelform_factory | from django.forms.models import modelform_factory | ||||||
|  | from django.db.models.deletion import ProtectedError | ||||||
|  |  | ||||||
| from .models import ( | from .models import ( | ||||||
|     Address, Place, Restaurant, Link, CharLink, TextLink, |     Address, Place, Restaurant, Link, CharLink, TextLink, | ||||||
|     Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company, |     Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company, | ||||||
|     Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D) |     Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D, | ||||||
|  |     Related, Content, Node, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class GenericRelationTests(TestCase): | class GenericRelationTests(TestCase): | ||||||
| @@ -223,3 +226,13 @@ class GenericRelationTests(TestCase): | |||||||
|         form.save() |         form.save() | ||||||
|         links = HasLinkThing._meta.get_field_by_name('links')[0].field |         links = HasLinkThing._meta.get_field_by_name('links')[0].field | ||||||
|         self.assertEqual(links.save_form_data_calls, 1) |         self.assertEqual(links.save_form_data_calls, 1) | ||||||
|  |  | ||||||
|  |     def test_ticket_22998(self): | ||||||
|  |         related = Related.objects.create() | ||||||
|  |         content = Content.objects.create(related_obj=related) | ||||||
|  |         node = Node.objects.create(content=content) | ||||||
|  |  | ||||||
|  |         # deleting the Related cascades to the Content cascades to the Node, | ||||||
|  |         # where the pre_delete signal should fire and prevent deletion. | ||||||
|  |         with self.assertRaises(ProtectedError): | ||||||
|  |             related.delete() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user