创建一个新模型,其中包含当前现有模型的所有字段

Posted

技术标签:

【中文标题】创建一个新模型,其中包含当前现有模型的所有字段【英文标题】:Create a new model which have all fields of currently existing model 【发布时间】:2015-02-01 00:58:32 【问题描述】:

我的 django 应用程序有一个模型 Player

class Player(models.Model):
    """ player model """
    name = models.CharField(max_length=100, null=True, blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    hash = models.CharField(max_length=128, null=True, blank=True)
    bookmark_url = models.CharField(max_length=300, null=True, blank=True)

根据我的要求,我需要创建一个新模型 BookmarkPlayer,它将包含 Player 模型的所有字段。

现在我有两件事要做。

    我可以为 BookmarkPlayer 模型扩展 Player 类。
类书签播放器(播放器): """只是一个书签播放器""" 元类: app_label = "核心"
    我可以将 Player 模型的所有字段定义到 BookmarkPlayer 模型中。
类书签播放器(模型。模型): """书签播放器模型""" name = models.CharField(max_length=100, null=True, blank=True) date_created = models.DateTimeField(auto_now_add=True) last_updated = models.DateTimeField(auto_now=True) hash = models.CharField(max_length=128, null=True, blank=True) bookmark_url = models.CharField(max_length=300, null=True, blank=True)

我只是想知道哪种方法更好。如果有其他好的方法,请与我分享。

更新问题

Knbb 创建基类的想法很有趣,但我面临的问题是我的一个模型已经存在于数据库中。

我的实际模型:

类地址(模型。模型): 地址 = models.TextField(空=真,空白=真) 类站点(模型。模型): domain = models.CharField(max_length=200) 类播放器(模型。模型): # ... 其他字段 shipping_address = models.ForeignKey(地址,related_name='shipping') billing_address = models.ForeignKey(地址,related_name='billing') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now_add=True) 站点 = 模型.ManyToManyField(站点,null=True,空白=True) 元类: 摘要 = 真

更改后的模型:

类地址(模型。模型): 地址 = models.TextField(空=真,空白=真) 类站点(模型。模型): domain = models.CharField(max_length=200) 类 BasePlayer(模型。模型): # .. 其他字段 shipping_address = models.ForeignKey(地址,related_name='shipping') billing_address = models.ForeignKey(地址,related_name='billing') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now_add=True) 站点 = 模型.ManyToManyField(站点,null=True,空白=True) 元类: 摘要 = 真 类播放器(BasePlayer): 元类: app_label = '核心' 类书签播放器(BasePlayer): 元类: app_label = '核心'

在我运行 django 服务器时进行这些更改后,我收到以下错误。

django.core.management.base.CommandError:一个或多个模型未验证: core.test1:字段“shipping_address”的访问器与相关字段“Address.shipping”发生冲突。将related_name 参数添加到“shipping_address”的定义中。 core.test1:字段“shipping_address”的反向查询名称与相关字段“Address.shipping”冲突。将related_name 参数添加到“shipping_address”的定义中。 core.test1:字段“billing_address”的访问器与相关字段“Address.billing”发生冲突。在“billing_address”的定义中添加一个related_name 参数。 core.test1:字段“billing_address”的反向查询名称与相关字段“Address.billing”冲突。在“billing_address”的定义中添加一个related_name 参数。 core.test2:字段“shipping_address”的访问器与相关字段“Address.shipping”发生冲突。将related_name 参数添加到“shipping_address”的定义中。 core.test2:字段“shipping_address”的反向查询名称与相关字段“Address.shipping”冲突。将related_name 参数添加到“shipping_address”的定义中。 core.test2:字段“billing_address”的访问器与相关字段“Address.billing”发生冲突。在“billing_address”的定义中添加一个related_name 参数。 core.test2:字段“billing_address”的反向查询名称与相关字段“Address.billing”冲突。将相关名称参数添加到“帐单地址”的定义中

答案: 最后,如果我们将 ForeignKey 或 ManyToManyField 上的 related_name 属性用于抽象模型,我得到了答案。 这通常会在抽象基类中引起问题,因为此类上的字段包含在每个子类中,并且每次的属性值(包括相关名称)完全相同。 要解决此问题,当您在抽象基类中使用related_name 时(仅),名称的一部分应包含“%(app_label)s”和“%(class)s”。https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes 现在我的 BasePlayer 模型是

类 BasePlayer(模型。模型): # .. 其他字段 shipping_address = models.ForeignKey(Address, related_name='%(app_label)s_%(class)s_shipping') billing_address = models.ForeignKey(Address, related_name='%(app_label)s_%(class)s_billing') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now_add=True) 站点 = 模型.ManyToManyField(站点,null=True,空白=True) 元类: 摘要 = 真

【问题讨论】:

还有其他几种更好的方法可以做到这一点,但这取决于您的确切要求,哪一种是最好的。你首先想要两个模型的原因是什么? 我想将播放器详细信息添加为书签,以便在我的网站上共享用户之间的会话。 所以如果我理解正确,您基本上是想在特定时刻复制Player 的详细信息并将其另存为BookmarkPlayer?在这种情况下,请参阅我的答案,那将是最好的方法。添加一些方便的方法也很容易将Player 转换为BookmarkPlayer,反之亦然。 【参考方案1】:

这里有一些关于模型继承的好信息: https://docs.djangoproject.com/en/1.7/topics/db/models/#model-inheritance

选项 2 会引入重复,这总是不好的。如果BookmarkPlayer 没有任何新字段,只有不同的方法,我建议您使用链接中描述的“代理模型”选项,因为您不需要BookmarkPlayer 在数据库中拥有自己的表。

如果它需要它自己的表,“多表继承”是要走的路,这将是您问题中的选项 1

【讨论】:

BookmarkPlayer 需要有自己的表进入数据库。 @PrashantGaur 我更新了答案以涵盖该场景 @knbk 当然是对的,多表继承只会将额外的BookmarkPlayer 字段保存在新表中。他的回答更好:) @PrashantGaur 我想将相关名称更改为不同的名称值得尝试。但它在我的机器上验证了。你使用的是哪个 django 版本? 我正在使用 Django1.3。请在我的问题中查看更新。我知道为什么会出现错误。【参考方案2】:

如果您的BookmarkPlayer 需要相同的数据但在不同的表中,则最好使用抽象基础模型:

class BasePlayer(models.Model):
    name = models.CharField(max_length=100, null=True, blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    hash = models.CharField(max_length=128, null=True, blank=True)
    bookmark_url = models.CharField(max_length=300, null=True, blank=True)

    class Meta:
        abstract = True

class Player(BasePlayer):
    """ player model """
    pass

class BookmarkPlayer(BasePlayer):
    """ bookmark player model """
    pass

这样,PlayerBookmarkPlayer 都从BasePlayer 模型中继承了它们的字段,但是因为BasePlayer 是抽象的,所以模型完全解耦了。

另一方面,多表继承仍会将字段保存在单个表中,但会为BookmarkPlayer 添加一个额外的表,并在Player 表中添加一个隐含的OneToOneField

【讨论】:

我尝试根据您的建议创建一个抽象性质的基类,但通过使用它我无法验证我的模型。请参阅更新后的问题

以上是关于创建一个新模型,其中包含当前现有模型的所有字段的主要内容,如果未能解决你的问题,请参考以下文章

Rails3:将一个数据库中的字段包含到另一个模型中

如何将字段添加到现有模型[重复]

代码优先Add-Migration仅选择对现有模型的更改而不是新模型

如何使用数据为现有模型中的新模型字段设置默认值

向现有 django 模型添加新的唯一字段时的最佳实践

Django Rest Framework 中的模型翻译