-
Notifications
You must be signed in to change notification settings - Fork 2.9k
#19680 fix deletion dependency order for GenericRelations #19681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
a23a0f2
#19680 fix deletion dependency order for GenericRelations
arthanson cf4f098
19680 add test
arthanson 21f3007
19680 fix Collector and test
arthanson fd9decc
19680 put on changeloggingmixin
arthanson ffae0df
19680 cleanup
arthanson aa21e0f
19680 cleanup
arthanson 5334fd7
19680 cleanup
arthanson 1c71107
19680 skip changelog update for deleted objects
arthanson 289d11c
19680 remove print
arthanson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import logging | ||
|
|
||
| from django.contrib.contenttypes.fields import GenericRelation | ||
| from django.db import router | ||
| from django.db.models.deletion import Collector | ||
|
|
||
| logger = logging.getLogger("netbox.models.deletion") | ||
|
|
||
|
|
||
| class CustomCollector(Collector): | ||
| """ | ||
| Custom collector that handles GenericRelations correctly. | ||
| """ | ||
|
|
||
| def collect( | ||
| self, | ||
| objs, | ||
| source=None, | ||
| nullable=False, | ||
| collect_related=True, | ||
| source_attr=None, | ||
| reverse_dependency=False, | ||
| keep_parents=False, | ||
| fail_on_restricted=True, | ||
| ): | ||
| """ | ||
| Override collect to first collect standard dependencies, | ||
| then add GenericRelations to the dependency graph. | ||
| """ | ||
| # Call parent collect first to get all standard dependencies | ||
| super().collect( | ||
| objs, | ||
| source=source, | ||
| nullable=nullable, | ||
| collect_related=collect_related, | ||
| source_attr=source_attr, | ||
| reverse_dependency=reverse_dependency, | ||
| keep_parents=keep_parents, | ||
| fail_on_restricted=fail_on_restricted, | ||
| ) | ||
|
|
||
| # Track which GenericRelations we've already processed to prevent infinite recursion | ||
| processed_relations = set() | ||
|
|
||
| # Now add GenericRelations to the dependency graph | ||
| for _, instances in list(self.data.items()): | ||
| for instance in instances: | ||
| # Get all GenericRelations for this model | ||
| for field in instance._meta.private_fields: | ||
| if isinstance(field, GenericRelation): | ||
| # Create a unique key for this relation | ||
| relation_key = f"{instance._meta.model_name}.{field.name}" | ||
| if relation_key in processed_relations: | ||
| continue | ||
| processed_relations.add(relation_key) | ||
|
|
||
| # Add the model that the generic relation points to as a dependency | ||
| self.add_dependency(field.related_model, instance, reverse_dependency=True) | ||
|
|
||
|
|
||
| class DeleteMixin: | ||
| """ | ||
| Mixin to override the model delete function to use our custom collector. | ||
| """ | ||
|
|
||
| def delete(self, using=None, keep_parents=False): | ||
| """ | ||
| Override delete to use our custom collector. | ||
| """ | ||
| using = using or router.db_for_write(self.__class__, instance=self) | ||
| assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % ( | ||
| self._meta.object_name, | ||
| self._meta.pk.attname, | ||
| ) | ||
|
|
||
| collector = CustomCollector(using=using) | ||
| collector.collect([self], keep_parents=keep_parents) | ||
|
|
||
| return collector.delete() | ||
|
|
||
| delete.alters_data = True | ||
|
|
||
| @classmethod | ||
| def verify_mro(cls, instance): | ||
| """ | ||
| Verify that this mixin is first in the MRO. | ||
| """ | ||
| mro = instance.__class__.__mro__ | ||
| if mro.index(cls) != 0: | ||
| raise RuntimeError(f"{cls.__name__} must be first in the MRO. Current MRO: {mro}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@arthanson , just to be clear, we only want to employee this mechanism for
ChangedLoggingMixinand no other, correct?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it only matters in that case. This changes the order that the delete signal handlers are sent out, but the only reason we care about that is what order the ObjectChange records are written out, so this only effects models derived from ChangeLoggingMixin.