11👍
I had a similar problem in the past and eventually found a satisfactory solution thanks to this answer.
By implementing an abstract class that stores the real class and have it inherited by your parent class, once can cast each parent class instance to the actual type. (The abstract class used in that answer is now available in django-model-utils.)
For example, once you have the abstract class defined (or if you have django-model-utils), you can simply do:
class Animal(InheritanceCastModel):
name= models.CharField(max_length=128)
class Person(Animal):
pants_size = models.IntegerField(null=True)
class Dog(Animal):
panting_rate = models.IntegerField(null=True)
Using it is trivial:
>>> from zoo.models import Animal, Person, Dog
>>> Animal(name='Malcolm').save()
>>> Person(name='Dave').save()
>>> Dog(name='Mr. Rufflesworth').save()
>>> for obj in Animal.objects.all():
... print obj.name, type(obj.cast())
...
Malcolm <class 'zoo.models.Animal'>
Dave <class 'zoo.models.Person'>
Mr. Rufflesworth <class 'zoo.models.Dog'>
18👍
We implemented our own cast() function that works quite well (Without ContentType’s):
class Base(models.Model):
"""
If your class needs the basics, like created date, modified date etc, then
inherit from this Base class.
"""
created = models.DateTimeField(_('Created'), auto_now_add=True)
modified = models.DateTimeField(_('Modified'), auto_now=True)
class Meta:
abstract = True
def __str__(self):
return '%s [%s]' % (self.__class__.__name__, self.id)
def get_class_name(self):
return str(self.__class__.__name__).lower()
def to_json(self, include_related=True):
return {
'id': self.id,
'created': self.created.isoformat(),
'modified': self.modified.isoformat(),
'class_name': self.__class__.__name__
}
def cast(self):
"""
This method is quite handy, it converts "self" into its correct child class. For example:
.. code-block:: python
class Fruit(models.Model):
name = models.CharField()
class Apple(Fruit):
pass
fruit = Fruit.objects.get(name='Granny Smith')
apple = fruit.cast()
:return self: A casted child class of self
"""
for name in dir(self):
try:
attr = getattr(self, name)
if isinstance(attr, self.__class__):
return attr
except:
pass
return self
- Buildout vs virtualenv + pip for django?
- Import RelatedManager from django.db.models.fields.related
- Django admin, filter objects for inline formset
- Meta.fields contains a field that isn't defined on this FilterSet: ****
10👍
Yes, this can be done — you just need to query the automatic reverse OneToOneField
relations. E.g.:
a = Animal.objects.select_related('person', 'dog')[0]
a = a.person or a.dog or a # whichever is not None
print a
print isinstance(a, Person)
The use of select_related
here allows this to be done in a single query, rather than having to test for DoesNotExist
exceptions when you access the subclass attributes/relations.
See also my answer here and the InheritanceManager
in django-model-utils for a more elegant/long-term solution.
We’re looking at ways of making this easier in Django’s core.
2👍
To solve this, consider using django-polymorphic. It supports automatic downcasting of inherited models, works with ForeignKeys/ManyToMany fields and integrates in the admin too.
- Specifying Readonly access for Django.db connection object
- Django proxy User model example
- Resize image maintaining aspect ratio AND making portrait and landscape images exact same size?
- Display user group in admin interface?
- Django : Static content not found