Django模型继承:创建现有实例的子实例(向下转换)?

时间:2010-10-31 19:46:19

标签: python django inheritance

我正在尝试集成第三方Django应用程序,该应用程序不幸地决定继承django.contrib.auth.models.User,这对于可插拔应用程序来说是一个很大的禁忌。引用Malcolm Tredinnick

  

更重要的是,就像在Python中一样,你不能“低调”   Django的模型继承。也就是说,如果您已经创建了用户   例如,你不能,如果没有在掩护下探讨,那就做到这一点   instance对应于您尚未创建的子类实例。

好吧,我正处于需要将此第三方应用程序与现有用户实例集成的情况。所以,如果假设我确实愿意在封面下捅,我有什么选择?我知道这不起作用:

extended_user = ExtendedUser(user_ptr_id=auth_user.pk)
extended_user.save()

没有例外,但它会破坏所有类型的东西,首先用空字符串覆盖django.contrib.auth.models.User的所有列...

7 个答案:

答案 0 :(得分:65)

这应该有效:

extended_user = ExtendedUser(user_ptr_id=auth_user.pk)
extended_user.__dict__.update(auth_user.__dict__)
extended_user.save()

这里你基本上只是将auth_user版本的值复制到extended_user版本中,然后重新保存它。不是很优雅,但它有效。

答案 1 :(得分:6)

如果你不喜欢__dict__.update解决方案,你可以这样做:

for field in parent_obj._meta.fields
    setattr(child_obj, field.attname, getattr(parent_obj, field.attname))

答案 2 :(得分:5)

我通过询问django-user邮件列表找到了这个答案:

https://groups.google.com/d/msg/django-users/02t83cuEbeg/JnPkriW-omQJ

这不是公共API的一部分,但您可以依赖Django如何在内部加载fixture。

parent = Restaurant.objects.get(name__iexact="Bob's Place").parent
bar = Bar(parent=parent, happy_hour=True)
bar.save_base(raw=True)

请记住,这可能会破坏任何新版本的Django。

答案 3 :(得分:2)

我正在使用Django 1.6,我的ExtendedUser模型来自OSQA(forum.models.user.User)。出于某些奇怪的原因,上述具有dict.__update__setattr的解决方案有时会失败。这可能与我拥有的其他一些模型有关,这些模型会对用户表施加约束。您可以尝试以下两种解决方法:

解决方法#1:

extended_user = ExtendedUser(user_ptr_id = user.pk)
extended_user.save() # save first time
extended_user.__dict__.update(user.__dict__)
extended_user.save() # save second time

解决方法#2:

extended_user = ExtendedUser(user_ptr_id = user.pk)
extended_user.__dict__.update(user.__dict__)
extended_user.id=None
extended_user.save()

也就是说,如果同时设置pkid,有时保存新的子实例会失败,但您可以设置pk,保存它,然后一切似乎都能正常工作

答案 4 :(得分: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(child_cls, parent_obj, 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)

create_child_from_parent_model(ExtendedUser, auth_user, {})

此方法的优点在于,被子级覆盖的方法不会被原始的父级方法替换。 对我来说,使用原始答案obj.__dict__.update()会导致异常,就像我在父类中使用FieldTracker中的model_utils一样。

答案 5 :(得分:0)

那这样的事情呢?

from django.forms.models import model_to_dict

auth_user_dict = model_to_dict(auth_user)
extended_user = ExtendedUser.objects.create(user_ptr=auth_user, **auth_user_dict)

答案 6 :(得分:-1)

@guetti的答案对我几乎没有任何帮助=>关键是parent_ptr

parent_object = parent_model.objects.get(pk=parent_id)  
new_child_object_with_existing_parent = Child(parent_ptr=parent, child_filed1='Nothing')
new_child_object_with_existing_parent.save()

我想在个人资料模型中为现有用户创建条目,我的模型就像

from django.contrib.auth.models import User as user_model
class Profile(user_model):
     bio = models.CharField(maxlength=1000)
     another_filed = models.CharField(maxlength=1000, null=True, blank=True)

在某个地方,如果现有用户不存在,我需要创建个人资料,因此我按照以下步骤进行操作

对我有用的示例

from meetings.user import Profile
from django.contrib.auth.models import User as user_model

user_object = user_model.objects.get(pk=3)  
profile_object = Profile(user_ptr=user_object, bio='some')
profile_object.save()