Наследование модели Django - создание дочерних объектов на основе родительского экземпляра
Я использую Django 4.0
У меня есть следующие модели:
class Parent(models.Model):
# fields ommitted for the sake of brevity
pass
class Child(Parent):
child_only_field = models.CharField(max_length=64)
код
p = Parent.objects.create(**fields_dict)
c1 = Child.objects.create(child_only_field='Hello 123', p) # Obviously won't work
# c2 = ...
Существует ли способ создания дочерних объектов из экземпляра родительского (без ручной "распаковки" полей родительского)?
Преобразование полей p
в dict с помощью model_to_dict
и p._meta.fields
.
from django.forms.models import model_to_dict
class Parent(models.Model):
# fields ommitted for the sake of brevity
def fields_dict(self):
return model_to_dict(
p,
fields=[
field.name
for field in self._meta.fields
if field.name in self.__dict__
and field.name != self._meta.pk.name
]
)
class Child(Parent):
child_only_field = models.CharField(max_length=64)
p = Parent.objects.create(**fields_dict)
c1 = Child.objects.create(child_only_field='Hello 123', **p.fields_dict())
это легко, если вы понимаете, как работает Django с implizit o2o relation.
class Parent(Model):
# fields
# implizit o2o
class Child(Parent):
child_only_field = models.CharField(max_length=64)
# explizit o2o
class Child_1(Parent):
parent = models.OneToOneField(Parent)
child_only_field = models.CharField(max_length=64)
Как сохранить или создать ребенка с помощью Implizit o2o relation.
1.Save child object by existed Parent class object DON'T USE IT IN NORMAL CODE.
parent = Parent.objects.get(pk=some_pk)
child = Child(child_only_field='Hello 123', pk=parent.pk)
child.save_base(raw=True) # important part of code
Model.save_base
- внутренний метод для сохранения информации только для дочерних полей. Но родительские данные уже должны существовать.
2.Сохраните созданный Parent как child. НЕ ИСПОЛЬЗУЙТЕ ЕГО В ОБЫЧНОМ КОДЕ:
parent = Parent.objects.get(pk=some_pk)
parent.child_only_field = 'Hello 123'
parent.save_base(class=Child)
Model.save_base
метод сохраняет информацию для всей цепочки наследования.
3.Сохраните созданного ребенка в нормальном виде. ПОЖАЛУЙСТА, ИСПОЛЬЗУЙТЕ ЕГО В ОБЫЧНОМ КОДЕ:
child = Child(child_only_field='Hello 123', **fields_dict)
child.save()
Я особенно не использую DataManager. Я могу сказать, что это скорее внутренний метод, чем публичный интерфейс.
Сохраните ребенка через DataManager.
child = Child.objects.create(child_only_field='Hello 123', **fields_dict)
.objects.create
сделал пункт 3. из моего ответа. Вы можете увидеть его в django.db.models.query.py
Надеюсь, это поможет.
Следующая 1-строка создаст новый словарь полей, содержащий только общие поля, в цикле Django _meta.fields
(не включает поля M2M), который затем можно передать дочернему экземпляру.
p = Parent.objects.create(**fields_dict)
new_fields_dict = {f.name: getattr(p, f.name) for f in p._meta.fields
if f in Child._meta.fields and f != p._meta.pk}
c = Child.objects.create(child_only_field='Hello 123', **new_fields_dict)
Вы можете сделать это, присвоив родительский экземпляр дочернему атрибуту parent_ptr
следующим образом:
p = Parent.objects.create(**fields_dict)
c1 = Child.objects.create(child_only_field='Hello 123', parent_ptr=p)
Но, очевидно, вам придется заполнить все специфические для родителя поля, потому что Django не будет распространять эти значения (что является неудобным поведением); И если в родительской модели есть nullable поле, которое инициализируется в fields_dict
, оно будет сброшено после создания c1
(также неудобно). Поэтому, если вы ищете способ использовать только родительский экземпляр и ничего больше, возможно, прямого решения нет.
Подробнее
При наследовании моделей путем определения модели как суперкласса, таблицы будут созданы следующим образом:
А родительский экземпляр доступен через атрибут parent_ptr
дочернего экземпляра, поэтому вы можете присвоить его при создании дочернего экземпляра, как в приведенном выше коде, а выполнение c1.parent_field = "Hello"
также изменит его значение в таблице Parent
.