mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +00:00 
			
		
		
		
	Fixed #22998 -- Updated the fast_delete logic for GFKs
This commit is contained in:
		
				
					committed by
					
						 Anssi Kääriäinen
						Anssi Kääriäinen
					
				
			
			
				
	
			
			
			
						parent
						
							28efafa24c
						
					
				
				
					commit
					6e2b82fdf6
				
			| @@ -138,9 +138,9 @@ class Collector(object): | ||||
|                 include_hidden=True, include_proxy_eq=True): | ||||
|             if related.field.rel.on_delete is not DO_NOTHING: | ||||
|                 return False | ||||
|         # GFK deletes | ||||
|         for relation in opts.many_to_many: | ||||
|             if not relation.rel.through: | ||||
|         for field in model._meta.virtual_fields: | ||||
|             if hasattr(field, 'bulk_related_objects'): | ||||
|                 # It's something like generic foreign key. | ||||
|                 return False | ||||
|         return True | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ from django.contrib.contenttypes.fields import ( | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.db import models | ||||
| from django.utils.encoding import python_2_unicode_compatible | ||||
| from django.db.models.deletion import ProtectedError | ||||
|  | ||||
|  | ||||
| __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', | ||||
| @@ -192,3 +193,26 @@ class D(models.Model): | ||||
|  | ||||
|     class Meta: | ||||
|         ordering = ('id',) | ||||
|  | ||||
|  | ||||
| # Ticket #22998 | ||||
|  | ||||
| class Node(models.Model): | ||||
|     content_type = models.ForeignKey(ContentType) | ||||
|     object_id = models.PositiveIntegerField() | ||||
|     content = GenericForeignKey('content_type', 'object_id') | ||||
|  | ||||
|  | ||||
| class Content(models.Model): | ||||
|     nodes = 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, Sum | ||||
| from django.db.utils import IntegrityError | ||||
| from django.test import TestCase, skipIfDBFeature | ||||
| from django.forms.models import modelform_factory | ||||
| from django.db.models.deletion import ProtectedError | ||||
|  | ||||
| from .models import ( | ||||
|     Address, Place, Restaurant, Link, CharLink, TextLink, | ||||
|     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): | ||||
| @@ -247,3 +250,13 @@ class GenericRelationTests(TestCase): | ||||
|         form.save() | ||||
|         links = HasLinkThing._meta.get_field_by_name('links')[0] | ||||
|         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