Let's say I have a base Generic class called person to group all other persons that extend it:
class Person(models.Model):
name = models.CharField(...)
And the class Worker, that extends the Person:
class Worker(Person):
card_id = models.CharField(...)
When I access all Person objects I also see the Workers, but lets say that from there I want to do this:
worker = Worker(name='Example', card_id='1234')
person = Person.objects.all().first()
Let's say that for some reason I want to use the Person class and call an Attribute o Worker:
[in] person.card_id
# Somehow expecting this result:
[out] '1234
Is it possible, is it viable and how do I do it?
CodePudding user response:
I'm afraid this is not possible, even when you can guarantee that the Person
object you retrieved (ie. Person.objects.all().first()
) is also a Worker
.
There three styles of inheritance possible in Django: Abstract Base classes, Proxy Models, and Multi-Table Inheritance
In this case, you are using Multi-Table Inheritance. As the name suggest, each model will receive its own table in the database and can therefore also be queried.
When querying for Person
objects, what you will end up with are Person
instances, not Worker
instances or any other child class instance. Since the card_id
field is not defined in the Person
model, but rather in one of its child models, you will get an error when trying to retrieve the card_id
value of Person
instance, even though the instance is also a Worker
.
However, if you know that your Person
instance is also a Worker
, you can retrieve it via:
person = Person.objects.all().first()
worker = person.worker
This works because the inheritance relationship creates a OneToOneField
between the child and each of its parents. However, note that this will give an error in case the person
instance is not actually a Worker
.
One library that might help is the InheritanceManager from django-model-utils
.
By attaching the InheritanceManager to your base class model such as:
from model_utils.managers import InheritanceManager
class Person(models.Model):
objects = InheritanceManager()
# ...
You can then use the .select_subclasses
filter to filter a queryset of the Parent model to only include the objects of a particular child class. For example to only select the persons that are also workers, you can do the following query:
persons_worker = Person.objects.select_subclasses(Worker)
and you should never run into troubles when trying to do retrieve the card_id
for any of the instances in persons_worker
.