jazzband / django-model-utils

Django model mixins and utilities.
https://django-model-utils.readthedocs.io
BSD 3-Clause "New" or "Revised" License
2.67k stars 364 forks source link

bulk deletion of subclass objects not working #4

Open benwhalley opened 13 years ago

benwhalley commented 13 years ago

If I create a series of models roughly like this:

class A(PolymorphicModel):
some fields ...

class B(A):
# no extra fields... just new methods

class C(A):
# no extra fields... just new methods
....

then create some objects:

B().save()
C().save()

and then try and delete them:

A.objects.all().delete()

then I get an error something like "'C' object has no attribute 'b_ptr'"

Whereas if I delete them individually:

[i.delete() for i in A.objects.all()] 
>[None, None]

then all is fine. I'm not sure if this is a bug or a feature, but it's frustrating because I'm going to have to override the admin to make deletion of inline objects work (I'm using B and C only to override methods on A, no additional fields are stored).

carljm commented 12 years ago

I'm not sure what code this applies to, since there is no PolymorphicModel in django-model-utils and never has been.

spatialbits commented 8 years ago

I am experiencing exactly this issue. When I define a custom Model Manager for my model that returns a filtered queryset I get pointer errors when I try to delete objects.

class AssetAbstract(models.Model):
...
    objects = PublishedAssetInheritanceObjectManager()
class PublishedAssetInheritanceObjectManager(InheritanceManagerMixin, models.Manager):
    def get_queryset(self):
        return super(PublishedAssetInheritanceObjectManager, self).get_queryset().filter(published=True)
spatialbits commented 8 years ago

@carljm hey is this issue reopenable?

spatialbits commented 8 years ago

@benwhalley, so 5 years later I run into the same issue. There wouldn't be any chance that A) you resolved it and B) you remember what you did 5 years ago?

puterleat commented 8 years ago

I can't remember the details, but I think came to the conclusion that subclassing non-abstract django models just wasn't worth it...

kezabelle commented 8 years ago

@spatialbits when you say "I get pointer errors" - do you mean there's an exception whose stacktrace you could add to this ticket?

spatialbits commented 8 years ago

yeah sorry, could have added that. I am finding that it's not just on a bulk delete. When I set published to False on a model instance and save it I get an error as per below:

>>> from heritage.models import HeritageAsset, SessionAsset
>>> s = SessionAsset.objects.get(id=5993)
>>> s.published=False
>>> s.save()

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/base.py", line 734, in save
    force_update=force_update, update_fields=update_fields)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/base.py", line 761, in save_base
    self._save_parents(cls, using, update_fields)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/base.py", line 786, in _save_parents
    self._save_table(cls=parent, using=using, update_fields=update_fields)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/base.py", line 846, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/base.py", line 885, in _do_insert
    using=using, raw=raw)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/query.py", line 920, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/models/sql/compiler.py", line 974, in execute_sql
    cursor.execute(sql, params)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/backends/utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/utils.py", line 98, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/utils/six.py", line 658, in reraise
    raise value.with_traceback(tb)
  File "/data/projects/overview-env/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: duplicate key value violates unique constraint "heritage_heritageasset_pkey"
DETAIL:  Key (secureasset_ptr_id)=(5993) already exists.

Model Definitions:

class AssetAbstract(models.Model):
    name = models.CharField(max_length=100, blank=False, null=False)
    published = models.BooleanField(default=True)

    objects = PublishedAssetInheritanceObjectManager()
    admin_objects = InheritanceManager()

    class Meta:
        abstract = True

class SecureAsset(AssetAbstract):
    file = models.FileField()
    class Meta:
        permissions = (
            ("view_secureasset", "Can view secure asset"),
        )

class HeritageAsset(SecureAsset):
    @property
    def storage_string(self):
        return "heritage_assets"

class SessionAsset(HeritageAsset):
    session = models.ForeignKey(Session)

And the object manager as posted above:

class PublishedAssetInheritanceObjectManager(InheritanceManagerMixin, models.Manager):
    def get_queryset(self):
        return super(PublishedAssetInheritanceObjectManager, self).get_queryset().filter(published=True)
Eraldo commented 1 year ago

@spatialbits From what I figured, the InheritanceManager can only be used when dealing with non-abstract models. (Since it uses the one-to-one field that is not there if you do abstract inheritance.)