I have a model Person
which stores all data about people. I also have a Client
model which extends Person. I have another extending model OtherPe
What you are doing is not possible as you do it, Django has specific rules for inheritance
The only possible schema is:
class Parent(models.Model):
class Meta:
abstract = True # MUST BE !!! This results in no relation generated in your DB
field0 = models.CharField(...
...
# here you're allowed to put some functions and some fields here
class Child(models.Model):
field1 = models.CharField(...
...
# Anything you want, this model will create a relation in your database with field0, field1, ...
class GrandChild(models.Model):
class Meta:
proxy = True # MUST BE !!! This results in no relation generated in your DB
# here you're not allowed to put DB fields, but you can override __init__ to change attributes of the fields: choices, default,... You also can add model methods.
This is because there is no DB inheritance in most DBGS. Thus you need to make you parent class abstract
!
You can't really do that with subclassing. When you subclass Person
, you're implicitly telling Django that you'll be creating subclasses, not Person
objects. It's a PITA to take a Person
and transmogrify it into a OtherPerson
later.
You probably want a OneToOneField
instead. Both Client
and OtherPerson
should be subclasses of models.Model
:
class Client(models.Model):
person = models.OneToOneField(Person, related_name="client")
# ...
class OtherPerson(models.Model):
person = models.OneToOneField(Person, related_name="other_person")
# ...
Then you can do things like:
pers = Person(...)
pers.save()
client = Client(person=pers, ...)
client.save()
other = OtherPerson(person=pers, ...)
other.save()
pers.other.termination_date = datetime.now()
pers.other.save()
See https://docs.djangoproject.com/en/dev/topics/db/examples/one_to_one/ for more.
Okay, I hate to answer my own question, especially since it is sort of a repeat of (Django model inheritance: create sub-instance of existing instance (downcast)?
@Daniel Roseman got me out of a jam AGAIN. Gotta love that guy!
person = Person.objects.get(id=<my_person_id>)
client = Client(person_ptr_id=person.id)
client.__dict__.update(person.__dict__)
client.save()
other_person = OtherPerson(person_ptr_id=person.id)
other_person.__dict__.update(person.__dict__)
other_person.save()
If I have an existing Client
and want to make an OtherPerson
from them, which is my exact use-case, I just do this:
client_id = <ID of Client/Person I want to create an OtherPerson with>
p = Person.objects.get(id=client_id)
o = OtherPerson(person_ptr_id=p.id) # Note Person.id and Client.id are the same.
o.__dict__.update(p.__dict__)
o.save()
Now the person shows up as a Client on the clients screen and as an OtherPerson on the other person screen. I can get the OtherPerson version of the Person which has all the OtherPerson details and functions or I can get a Client version of that Person which has all the Client details and functions.
As mentioned in a comment already, there is an open ticket for this very question: https://code.djangoproject.com/ticket/7623
In the meanwhile there is a proposed patch (https://github.com/django/django/compare/master...ar45:child_object_from_parent_model) which not using obj.__dict__
but creates an dictionary with all field values cycling over all fields.
Here a simplified function:
def create_child_from_parent_model(parent_obj, child_cls, init_values: dict):
attrs = {}
for field in parent_obj._meta._get_fields(reverse=False, include_parents=True):
if field.attname not in attrs:
attrs[field.attname] = getattr(parent_obj, field.attname)
attrs[child_cls._meta.parents[parent_obj.__class__].name] = parent_obj
attrs.update(init_values)
print(attrs)
return child_cls(**attrs)
person = Person.objects.get(id=<my_person_id>)
client = create_child_from_parent_model(person, Client, {})
client.save()
If you want to create a sibling:
client_person = getattr(person, person._meta.parents.get(Person).name)
other_person = create_child_from_parent_model(person, OhterPerson, {})
other_person.save()
This method has the advantage that methods that are overwritten by the child are not replaced by the original parent methods.
For me using the original answers obj.__dict__.update()
led to exceptions as I was using the FieldTracker
from model_utils
in the parent class.