我有一个模型Person
,它存储有关人员的所有数据。我还有一个Client
模型,它扩展了Person。我有另一个扩展模型OtherPerson
,它还扩展了Person
模型。我想创建一个指向Person
的客户端,并且还创建一个指向OtherPerson
的{{1}}记录。基本上,我希望将一个Person
对象视为Person
和Client
,具体取决于当前视图。这是否可以使用Django的ORM,或者我是否需要以某种方式编写Raw查询来创建此场景。我很确定它可以从数据库方面进行,因为两个子类只会指向具有person_ptr_id字段的父Person类。
简单地说,如果我创建OtherPerson
(以及Client
),我还可以使用{{1}中的基础Person
创建OtherPerson
对象吗? }}。这样我可以将其视为Person
或Client
,保存一个会影响每个Client
字段吗?
“水平”多态?
以下是我的模型的简化版本,以便澄清:
OtherPerson
答案 0 :(得分:3)
好吧,我讨厌回答我自己的问题,特别是因为它有点重复(Django model inheritance: create sub-instance of existing instance (downcast)?
@Daniel Roseman再次让我脱离果酱。得爱那个人!
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()
如果我有一个Client
,并希望从他们那里制作一个OtherPerson
,这是我的确切用例,我就是这样做的:
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()
现在,此人在客户端屏幕上显示为客户端,在另一个人屏幕上显示为OtherPerson。我可以获得具有所有OtherPerson详细信息和功能的Person的OtherPerson版本,或者我可以获得具有所有客户端详细信息和功能的该Person的客户端版本。
答案 1 :(得分:2)
你正在做的事情是不可能的,Django有特定的继承规则
唯一可能的架构是:
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.
这是因为大多数DBGS都没有DB继承。因此,您需要使您成为父类abstract
!
答案 2 :(得分:1)
你不能用子类化来做到这一点。当你继承Person
时,你隐含地告诉Django你将创建子类,而不是Person
个对象。这是一个PITA,可以将Person
转换为OtherPerson
以后的OneToOneField
。
您可能需要Client
。 OtherPerson
和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")
# ...
的子类:
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()
然后你可以做以下事情:
{{1}}
有关详情,请参阅https://docs.djangoproject.com/en/dev/topics/db/examples/one_to_one/。
答案 3 :(得分:1)
正如已经在评论中提到的,有一个针对这个问题的公开票: https://code.djangoproject.com/ticket/7623
与此同时,有一个建议的补丁(https://github.com/django/django/compare/master...ar45:child_object_from_parent_model)没有使用obj.__dict__
但会创建一个字典,其中所有字段值都在所有字段上循环。
这是一个简化的功能:
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()
如果要创建同级:
client_person = getattr(person, person._meta.parents.get(Person).name)
other_person = create_child_from_parent_model(person, OhterPerson, {})
other_person.save()
此方法的优点在于,被子级覆盖的方法不会被原始的父级方法替换。
对我来说,使用原始答案obj.__dict__.update()
会导致异常,就像我在父类中使用FieldTracker
中的model_utils
一样。