Skip to content
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

Deletion doesn't function with self referential foreign key #540

Open
will-ockmore opened this issue Apr 24, 2023 · 0 comments
Open

Deletion doesn't function with self referential foreign key #540

will-ockmore opened this issue Apr 24, 2023 · 0 comments

Comments

@will-ockmore
Copy link

Django's .delete() functionality doesn't work when a polymorphic model subclass is referenced by an instance of the base class using a self referential foreign key. See below for model definitions and code to reproduce issue.

# myapp/models.py

from django.db import models
from polymorphic.models import PolymorphicModel


class A(PolymorphicModel):
    self_referential = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)

class B(A):
    name = models.CharField(max_length=256)

### script.py

from myapp.models import A, B

b = B.objects.create(self_referential=None, name="b")
a = A.objects.create(self_referential=b)

print(hasattr(a, "a_ptr"))
print(hasattr(b, "a_ptr"))

A.objects.all().delete()

Running the above results in the following output:

False
True
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File /script.py:29
     26 print(hasattr(a, "a_ptr"))
     27 print(hasattr(b, "a_ptr"))
---> 29 A.objects.all().delete()
     31 # %%

File /.venv/lib/python3.10/site-packages/django/db/models/query.py:745, in QuerySet.delete(self)
    742 del_query.query.clear_ordering(force_empty=True)
    744 collector = Collector(using=del_query.db)
--> 745 collector.collect(del_query)
    746 deleted, _rows_count = collector.delete()
    748 # Clear the result cache, in case this QuerySet gets reused.

File /.venv/lib/python3.10/site-packages/django/db/models/deletion.py:256, in Collector.collect(self, objs, source, nullable, collect_related, source_attr, reverse_dependency, keep_parents, fail_on_restricted)
    254     for ptr in concrete_model._meta.parents.values():
    255         if ptr:
--> 256             parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
    257             self.collect(parent_objs, source=model,
    258                          source_attr=ptr.remote_field.related_name,
    259                          collect_related=False,
    260                          reverse_dependency=True,
    261                          fail_on_restricted=False)
    262 if not collect_related:

File /.venv/lib/python3.10/site-packages/django/db/models/deletion.py:256, in <listcomp>(.0)
    254     for ptr in concrete_model._meta.parents.values():
    255         if ptr:
--> 256             parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
    257             self.collect(parent_objs, source=model,
    258                          source_attr=ptr.remote_field.related_name,
    259                          collect_related=False,
    260                          reverse_dependency=True,
    261                          fail_on_restricted=False)
    262 if not collect_related:

AttributeError: 'A' object has no attribute 'a_ptr'

Package versions

❯ poetry export -f requirements.txt | rg '^django==|polymorphic'
django-polymorphic==3.1.0 ; python_version >= "3.10" and python_version < "3.11" \
django==3.2.18 ; python_version >= "3.10" and python_version < "3.11" \
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant