Reputation: 240
Assuming I have the following models:
class SomeSuperClass(models.Model):
...
class SomeSubClassA(SomeSuperClass)
...
class SomeSubClassB(SomeSuperClass)
...
class SomeConnector(models.Model):
reference = models.ForeignKey(SomeSuperClass, on_delete=models.CASCADE)
...
Now what I would want to have is somehow when iterating over objects of SomeConnector
I always want to have right away objects of the respective subclasses, not of the superclasses. E.g.
for sc in SomeConnector.objects.all():
# somehow get the correct subclass of this `reference` field here,
# assuming it to be callable under sc.reference_correct_subclass:
print(sc.reference_correct_subclass.__class__.__name__)
could produce for example:
'SomeSubClassA'
'SomeSubClassB'
'SomeSubClassA'
'SomeSubClassA'
But never should an object of the superclass be used.
I know of django-model-utils and I could do something similiar by querying directly on the superclass, like this:
SomeSuperClass.objects_inheritance.select_subclasses()
where objects_inheritance
is the InheritanceManager
attached to SomeSuperClass
. However I could not figure out yet how to reproduce this when the superclass is used as foreign key in another class which I want to use for querying.
Upvotes: 4
Views: 697
Reputation: 240
I could get it working by subclasing the ForeignKey
field with an additionally sublcassed ForwardManyToOneDescriptor
as I found in this thread.
The code for this subclassing would be this:
from django.db.models.fields.related_descriptors import ForwardManyToOneDescriptor
class InheritanceForwardManyToOneDescriptor(ForwardManyToOneDescriptor):
def get_queryset(self, **hints):
return self.field.remote_field.model.objects_inheritance.db_manager(hints=hints).select_subclasses()
class InheritanceForeignKey(models.ForeignKey):
forward_related_accessor_class = InheritanceForwardManyToOneDescriptor
And to use it in my code example, then this would be integrated like this:
class SomeConnector(models.Model):
reference = InheritanceForeignKey(SomeSuperClass, on_delete=models.CASCADE)
Upvotes: 0