django-polymorphic icon indicating copy to clipboard operation
django-polymorphic copied to clipboard

How to use abstract base model classes with intermediate abstract classes

Open specialorange opened this issue 6 years ago • 3 comments

Problem (help wanted) - not sure how to change the label to help wanted

I am trying to understand how to query an abstract base class/model via a unique model field (guaranteed unique by each end concrete model sharing the same unique_together clause(s) ). I changed the highest Parent class to the PolymorphicModel. I migrated the database using a migration written by django, then modified by me, and then migrated, and see the successful database (both django_content_types and the extra column on the concrete models).

Environment

  • Django-polymorphic version: current - 545bf7d4
  • Django version: 2.1
  • Python version: 3.5 and 3.6
  • Other libraries used, if any: none to my knowledge that involve the model classes outside of native django

Code examples

// models.py

class Asset(PolymorphicModel):
    asset_id = models.CharField(max_length=100, editable=False)  # argua3y4tv9yqawnqv29…
    asset_name = models.CharField(max_length=200, editable=False)  # 10101
    account = models.ForeignKey(Account, on_delete=models.CASCADE)

    class Meta(object):
        abstract = True

class B(Asset):  # intermediate abstract class
    pass

    class Meta(object):
        abstract = True

class C(B):
    unique_feature = models.CharField(max_length=200)
    class Meta(object):
        unique_together = (('asset_id', 'account'))

class D(B):
    cake = models.CharField(max_length=200)
    class Meta(object):
        unique_together = (('asset_id', 'account'))
    

// urls.py

    url(r'^accounts/(?P<slug>[a-zA-Z]+)/assets/(?P<assetName>[a-zA-Z0-9]+)',
        AssetsViewSet.as_view({'get': 'retrieve'})),

// views.py (from a DRF viewset)

class AssetsViewSet(viewsets.ModelViewSet):
    serializer_class = AssetSerializer
    permission_classes = (permissions.IsAuthenticated, IsOrganizationMember,)


    def retrieve(self, request, slug=None, assetName=None):
        /regardless if I filter by the asset's account foregin key
        queryset = Asset.objects.filter(asset_name=assetName).select_subclasses()

This gives me the error in the browser:

AttributeError at /api/v1/accounts/FSL/assets/10101 Manager isn't available; Asset is abstract

If I remove the abstract class definition on the parent class (Asset), then the django checker during compile errors for each of the concrete classes:

app.C: (models.E016) 'unique_together' refers to field 'account' which is not local to model 'C'.
  HINT: This issue may be caused by multi-table inheritance.
app.C: (models.E016) 'unique_together' refers to field 'asset_id' which is not local to model 'C'.
  HINT: This issue may be caused by multi-table inheritance.

I am unsure how to go about keeping the database structure of what I have (Abstract tree with many intermediate abstract models and some concrete models and to be able to query against the highest [grand]Parent abstract base class.

specialorange avatar Oct 17 '18 04:10 specialorange

unique_together is enforced at the database level, so I don't think it's possible to have the constraint set on the inherited models of a non-abstract parent. Perhaps you can override the validate_unique method on the child models instead?

mumumumu avatar Oct 17 '18 19:10 mumumumu

Thanks @mumumumu , I thought my uniqueness is currently working, maybe through the validations already, but I think the uniqueness is irrespective o trying to get the polymorphism to work.

specialorange avatar Oct 19 '18 17:10 specialorange

I don't think you can have abstract base classes with django-polymorphic tho. Generally, the superclass has a column describing the subclass for each row (and additional SQL is emitted base on that).

If your base class is abstract, there's no table for that, so I don't see how polymorphic could figure out subclasses.

WhyNotHugo avatar Oct 14 '19 17:10 WhyNotHugo