【问题标题】:Django model inheritance: create sub-instance of existing instance (downcast)?Django 模型继承:创建现有实例的子实例(向下转型)?
【发布时间】:2011-05-03 03:55:29
【问题描述】:

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

不过,更重要的是,就像在 Python 中一样,您不能“沮丧”地使用 Django 的模型继承。也就是说,如果您已经创建了用户 例如,你不能不偷偷摸摸地做那个 instance 对应于您尚未创建的子类实例。

嗯,我需要将此第 3 方应用程序与我现有的用户实例集成。所以,如果假设我确实愿意在被窝里闲逛,我有什么选择?我知道这不起作用:

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

也不例外,但它破坏了各种东西,首先用空字符串覆盖django.contrib.auth.models.User 中的所有列...

【问题讨论】:

标签: python django inheritance


【解决方案1】:

这应该可行:

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

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

【讨论】:

  • 太好了,谢谢,丹尼尔。让我摆脱困境:stackoverflow.com/questions/18130124/….
  • 您应该从字典中排除 pk 或在保存之前将其重置为 None。另见另一个答案。否则,您只是在等待 IntegrityError。
  • 人终于解决了。在这个问题上卡了太久。谢谢
  • 有谁知道如何在不重新保存/更新所有字段的情况下做到这一点?我试图在保存中指定 select update_fields,但我不知道我需要什么。我已经尝试过(pk、id、user_ptr_id 和 parent)和一堆更牵强的,但没有一个工作!
  • 通过管理员操作使用时,这给了我一个错误“无法设置属性”
【解决方案2】:

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

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

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

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

请记住,这可能会与任何新版本的 Django 中断。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      我使用的是 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,保存它,然后一切似乎都正常工作。

      【讨论】:

        【解决方案5】:

        这个问题有一个未解决的错误: 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() 会导致异常,因为我在父类中使用来自 model_utilsFieldTracker

        【讨论】:

        • 这似乎适用于除多对多字段之外的所有字段,不幸的是,这对我来说是个问题。如果是这种情况该怎么办?
        • 它保留了 PK,因此对于多线程应该可以正常工作,除非您的意思是您的 m2m 指向子模型,在这种情况下,您需要将其更改为指向另一个模型。
        【解决方案6】:

        这样的事情怎么样:

        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)
        

        【讨论】:

          【解决方案7】:

          @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()
          

          【讨论】:

            猜你喜欢
            • 2012-12-11
            • 1970-01-01
            • 1970-01-01
            • 2013-04-01
            • 1970-01-01
            • 2013-02-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多