How to access a child models or classes in Django, or how to cast models in Django.
https://django-polymorphic.readthedocs.org/en/latest/
""""""
The bellow text will be deleted
""""""
Let's think by giving a actual example...
models.py
from django.db import models class Person(models.Model): name = models.CharField(max_length=200) class Student(Person): student_num = models.IntegerField(default=100) class Teacher(Person): teacher_num = models.IntegerField(default=100)
1. a most easy way...
Let think about one object which you get by Model.objects.get method. And if you know which child class this object is belong to. Then, you can access child class object by just writting object.lower_case_child_class_name.
>>> Student(name="Tom", student_num=99).save() >>> person = Person.objects.get(name="Tom") >>> # person.student_num <= ObjectDoesNotExist Error would occur... >>> student = person.student # object.lower_case_child_model_name >>> student.student_num 99
2. get_child_object method
I've wrote this paragraph by referring at djangosnippets: Get child model
Let think about one object which you get by Model.objects.get method. And if you don't know which child class this object is belong to. Then, get_child_object method which I have wrote might be helpful.
usage
>>> from polls.models import Person >>> from polls.models import Student >>> from polls.models import Teacher >>> >>> Student(name="Tom", student_num="1").save() >>> Teacher(name="Malco", teacher_num="1").save() >>> >>> person_list = Person.objects.all() >>> >>> person_list[0] <Person: Person object> >>> person_list[0].student <Student: Student object> >>> person_list[0].teacher Traceback (most recent call last): ... polls.models.DoesNotExist: Person has no teacher. >>> >>> get_child_object(person_list[0]) <Student: Student object> >>> get_child_object(person_list[1]) <Teacher: Teacher object> >>>
get_child_object method
from django.core.exceptions import ObjectDoesNotExist class MultipleChildObjectsReturned(Exception): def __init__(self, len_child_objects): self.len_child_objects = len_child_objects def __str__(self): return repr(self.len_child_objects) def get_child_object(parental_object): child_objects = [] all_field_names = parental_object._meta.get_all_field_names() for field_name in all_field_names: field = None child_object = None try: field = parental_object.__getattribute__(field_name) except ObjectDoesNotExist: continue if issubclass(field.__class__, parental_object.__class__): child_object = field else: continue if field_name == child_object.__class__.__name__.lower(): child_objects.append(child_object) else: continue if len(child_objects) == 1: return child_objects[0] else: raise MultipleChildObjectsReturned(len(child_objects))
Q. Is it safe? or well tested?
A. I'm considering now...
Some description...
Model._meta.get_all_field_names method
>>> person0 = person_list[0] >>> person1 = person_list[1] >>> >>> person0._meta.get_all_field_names() ['id', 'name', 'student', 'teacher'] >>> >>> person0.__dict__ {'_state': <django.db.models.base.ModelState object at 0x10cda9ad0>, 'name': 'Tom', 'id': 1, '_teacher_cache': None, '_student_cache': <Student: Student object>}
Model.__getattribute__ method
>>> person0.__getattribute__('id') 1 >>> >>> person0.__getattribute__('name') 'Tom' >>> >>> person0.__getattribute__('student') <Student: Student object> >>> >>> person0.__getattribute__('teacher') Traceback (most recent call last): File "<console>", line 1, in <module> ... self.related.get_accessor_name())) myapp.models.DoesNotExist: Person has no teacher. >>>
And, if you want to use the child model's field in the parent,
just use self.child_model_field
class Person(models.Model): name = models.CharField(max_length=200) def print_student_num(self): try: self.student_num except NameError: print(self.__class__.__name__ + " " + "should be inherited") print("by a model which has the field named student_num."): raise else: return self.student_num
(`・ω・´)