Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model inheritance - creating child objects based on parent instance

I am using Django 4.0

I have the following models:

class Parent(models.Model):
    # fields ommitted for the sake of brevity
    pass

class Child(Parent):
   child_only_field = models.CharField(max_length=64)

code

p = Parent.objects.create(**fields_dict)
c1 = Child.objects.create(child_only_field='Hello 123', p) # Obviously won't work
# c2 = ...

Is there a way to create child objects from an instance of a parent (without manually "unpacking" the fields of the parent)?

like image 503
Homunculus Reticulli Avatar asked May 23 '26 13:05

Homunculus Reticulli


2 Answers

it is easy, if you understand how Django works with 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)

How to save or create child with 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 - internal method to save information only for child fields. BUT parent data should already exists.

2.Save created Parent as child. DON'T USE IT IN NORMAL CODE:

parent = Parent.objects.get(pk=some_pk)
parent.child_only_field = 'Hello 123'
parent.save_base(class=Child)

Model.save_base method saves information for whole chain of inheritance.

3.Save created child normally. PLEASE USE IT IN NORMAL CODE:

child = Child(child_only_field='Hello 123', **fields_dict)
child.save()

I especially don't use DataManager. I can say, this is more as internal method, than public interface.

  1. Save child through DataManager.

    child = Child.objects.create(child_only_field='Hello 123', **fields_dict)

    objects.create made point 3. from my answer. You can see it in django.db.models.query.py

I hope it helps.

like image 178
Maxim Danilov Avatar answered May 26 '26 01:05

Maxim Danilov


The following 1-liner will produce a new field dictionary containing only shared fields, looping through Django's _meta.fields (does not include M2M fields), which you can then pass to the child instance.

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)
like image 34
richardk Avatar answered May 26 '26 01:05

richardk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!