Django Model OneToOneField 无需创建额外的 _id 数据库列

Posted

技术标签:

【中文标题】Django Model OneToOneField 无需创建额外的 _id 数据库列【英文标题】:Django Model OneToOneField without creating additional _id database column 【发布时间】:2014-11-29 02:31:33 【问题描述】:

我正在使用 Account 模型,我想将它与 Settings 模型内部连接,而不必在我的 Account 模型中创建额外的 settings_id 列,因为 Account 表上的 PK 与设置上的 PK准确的表。我一直在尝试在这些表和列之间建立 OneToOne 关系,但不知道如何在不必创建新列或其他东西的情况下做到这一点。我正在尝试尽可能多地使用现有架构,并且不希望创建不必要的列。

Class Account(models.Model):
    login_id = models.AutoField(primary_key=True)
    settings = models.OneToOneField('Settings', to_field='login_id', null=True,
        related_name='settings', db_column='login_id') # Doesn't work because login_id already exists....


Class Settings(model.Model):
    login_id = models.OneToOneField('Account', to_field='login_id', related_name='account')

基本上,我要复制的查询是:

SELECT * FROM account
INNER JOIN settings
ON account.login_id=settings.login_id
WHERE account.login_id=1

基于错误,Django 的 ORM 似乎确实坚持在 Account 表中创建新列,但是当 2 个表之间的关系如此简单时,我看不出添加新列的意义:account.login_id = settings.login_id

【问题讨论】:

为什么两个模型相互指向是一对一的关系?您不需要在双方都指定。在其中任何一个上删除它。可通过 ORM 访问的正向和反向关系 既然他们是相关的,这似乎是合乎逻辑的。我认为这不会导致我正在创建的错误,但我会尝试从“设置”模型中删除关系,看看是否有帮助。 这将是问题的一部分。另一件事是 - 你到底想达到什么目的?在典型情况下,您似乎不必运行这样的查询。 我有每个用户的帐户表和每个用户的设置表。这些表上的 PK 是相同的。我正在处理应用程序的设置部分,我们将在功能完成后为每个用户回填设置。因此,尚未为每个用户提供设置。我想设置模型以便强制执行相同的 PK(我认为双方的 OneToOne 会这样做) 同意。这是实现此目的的一种方法(至少让您入门):***.com/questions/2846029/… - 在保存时检查对象是否存在,如果不存在,则创建并分配。 【参考方案1】:

虽然我自己从未尝试过,但将 OneToOneField 声明为主键应该可以。

class Account(models.Model):
    login_id = models.AutoField(primary_key=True)

class Settings(model.Model):
    account = models.OneToOneField('Account', to_field='login_id', 
        primary_key=True, related_name='settings')

声明primary_key 将阻止Django 尝试为您创建主键列,并且通过此设置,您可以访问account.settingssettings.accountsettings.account_id

【讨论】:

谢谢,这就是答案。我对related_name 的工作原理没有完全了解。使用我正在使用的架构,我不需要使用您提出的 primary_key=True 建议。 对我来说很奇怪,您可以基本上从另一个模型的属性中的参数定义一个模型的属性。看起来它会构建一个非常大的代码库,其中包含很多模型关系,让人有点难以理解。 @CoryDanielson:明确不重复自己之间存在着内在的张力。 Django 一直更喜欢 DRY,而 SQLAlchemy 让你在关系的两端定义字段(我认为)。 Django 的方法对我来说是正确的;另一方面,在 ManyToManyField 的情况下,在幕后创建了一个全新的表,我几乎总是更喜欢明确地设置该表。 是的,我想命名约定应该足够强大,以至于您不必考虑从什么模型/表派生属性。我想我只是带着更明确的期望进入 Django。【参考方案2】:

这是我最终做的:

class Account(models.Model):
    login_id = models.AutoField(primary_key=True)

    def get_settings_dict(self):
        try:
            return self.settings.to_dict()
        except Settings.DoesNotExist:
            return Settings.get_defaults_dict()

class Settings(model.Model):
    account = models.OneToOneField('Account', to_field='login_id', related_name='settings')
    # other settings properties
    # other settings properties

    def to_dict(self):
        # return current values as dict

    @staticmethod
    def get_defaults_dict():
        # return default values dict

这使我可以通过account.settings 访问用户的设置,而无需在帐户模型上创建额外的settings_id 列。两个模型都不需要 OneToOne 关系。

此外,当用户的设置尚不存在时,get_settings_dict 方法会返回默认设置。这是一个很好的便利,让我不必在每次通过account.settings 访问用户设置时都进行空检查。

【讨论】:

以上是关于Django Model OneToOneField 无需创建额外的 _id 数据库列的主要内容,如果未能解决你的问题,请参考以下文章

python django应用中的model啥作用

django1.8 model :Model syntax

Django中的model继承

Django model 操作

django 有model生成SQL以及现有反向表生成model

django创建model