不需要参照完整性的Django ForeignKey?
Posted
技术标签:
【中文标题】不需要参照完整性的Django ForeignKey?【英文标题】:Django ForeignKey which does not require referential integrity? 【发布时间】:2011-04-03 07:13:54 【问题描述】:我想在 django 模型中设置一个 ForeignKey
字段,该字段有时会指向另一个表。但是我希望可以在该字段中插入一个 id,该字段引用另一个表中可能不存在的条目。因此,如果该行存在于另一个表中,我想获得 ForeignKey 关系的所有好处。但如果不是,我希望这只是一个数字。
这可能吗?这就是通用关系的用途吗?
【问题讨论】:
这有帮助吗? djangoproject.com/documentation/models/many_to_one_null 这很相似,但不是我想要的。我想在字段中输入一个数字,而不是让该字段为空。但该数字与引用表中的对象不对应。 你能告诉我们更多关于用例的信息吗?与其滥用 ForeignKey 概念,或许还有更优雅的解决方案。 下面的SSN评论完全正确。我们正在从外部来源收集数据,其中包括我们目前可能或可能不知道的其他实体的 ID 值。 ID 值是稳定的。我们稍后可能会了解这些 ID 所指的实体。 Hey Leopd - 我遇到了同样的问题,因为你使用它已经有一段时间了,我假设你没有不良的副作用。您是否基本上在模型中声明了 ForeignKey 关系,然后设置 managed=False?只是好奇。在看到这个之前我也问了一个类似的问题——***.com/questions/6788558/… 【参考方案1】:tablename= columnname.ForeignKey('table', null=True, blank=True, db_constraint=False)
在你的程序中使用它
【讨论】:
这个方案已经在批准的answer中提出了,为什么要重复呢?【参考方案2】:这个问题是很久以前提出的,但对于新手来说,现在有一种内置方法可以通过在 ForeignKey 上设置 db_constraint=False 来处理这个问题:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.db_constraint
customer = models.ForeignKey('Customer', db_constraint=False)
或者,如果您想要为 null 并且不强制执行参照完整性:
customer = models.ForeignKey('Customer', null=True, blank=True, db_constraint=False)
我们在无法保证关系将以正确的顺序创建的情况下使用。
编辑:更新链接
【讨论】:
澄清这一点,因为作为一个新手,这让我有点困惑。至少对于 Oracle,Django 设置 FK,以便它们只检查提交时的约束(约束“DEFERRABLE INITIALLY DEFERRED”)。因此,如果您可以在一个事务中插入无序的 FK 关系,则无需禁用 FK 约束。【参考方案3】:要由@Glenn Maynard 通过 South 解决,生成一个空的 South 迁移:
python manage.py schemamigration myapp name_of_migration --empty
编辑迁移文件然后运行它:
def forwards(self, orm):
db.delete_foreign_key('table_name', 'field_name')
def backwards(self, orm):
sql = db.foreign_key_sql('table_name', 'field_name', 'foreign_table_name', 'foreign_field_name')
db.execute(sql)
Source article
【讨论】:
【参考方案4】:(注意:如果您解释为什么要这样做可能会有所帮助。可能有更好的方法来解决潜在问题。)
这可能吗?
不单独使用 ForeignKey,因为您正在重载具有两种不同含义的列值,而没有可靠的方式来区分它们。 (例如,如果在目标表中创建新条目时使用与引用表中旧条目匹配的主键会发生什么情况?当新目标条目被删除时,这些旧引用条目会发生什么情况?)
通常的临时解决方案是在外键旁边定义一个“类型”或“标签”列,以区分不同的含义(但见下文)。
这就是通用关系的用途吗?
是的,部分。
GenericForeignKey 只是上述模式的 Django 便利助手;它将外键与类型标签配对,该标签标识它所引用的表/模型(使用模型的关联 ContentType;参见contenttypes)
例子:
class Foo(models.Model):
other_type = models.ForeignKey('contenttypes.ContentType', null=True)
other_id = models.PositiveIntegerField()
# Optional accessor, not a stored column
other = generic.GenericForeignKey('other_type', 'other_id')
这将允许您像 ForeignKey 一样使用 other
来引用其他模型的实例。 (在后台,GenericForeignKey 为您获取并设置other_type
和other_id
。)
要表示一个不是引用的数字,您可以将other_type
设置为None,然后直接使用other_id
。在这种情况下,尝试访问 other
将始终返回 None,而不是引发 DoesNotExist
(或由于 id 冲突而返回意外对象)。
【讨论】:
如果您希望能够引用多个表中的行,这很有用,但我认为这是不同的。这种情况似乎更接近于拥有一个外部唯一 ID,例如 SSN,它可能在外部表中有也可能没有条目,并且以后可以在其中创建该条目。换句话说,id always 指的是命名表中的逻辑条目;这些条目并不总是存在。 (比如后面加了外行,关系应该是完整的,不用仔细更新other_type。) Glenn 关于数据模型应该如何工作的观点是正确的。因此,为了回答您的问题,如果在目标表中创建了一个与 FK-like 字段中的 ID 匹配的新条目:太好了!现在我们有一个参考。在您的解决方案中,在每次插入时,我都必须在源表中搜索与该 ID 匹配的内容并更新类型列。其他 Q——如果目标表中的条目被删除:什么也没有发生;不要级联删除。 Leopd:啊,谢谢你的澄清。在这种情况下,类型化的引用是不相关的,是的,您可能希望继承或自定义ForeignKey
以实现您想要的行为:它可能会被制成可重用的 OptionalForeignKey
或 SoftForeignKey
字段。跨度>
【参考方案5】:
这可能就像声明 ForeignKey 并创建列而不实际将其声明为 FOREIGN KEY 一样简单。这样,您将获得 o.obj_id,如果对象存在,o.obj 将起作用,并且 - 我认为 - 如果您尝试加载实际不存在的对象(可能是 DoesNotExist
),则会引发异常.
但是,我认为没有任何方法可以让syncdb
为您执行此操作。我发现syncdb
被限制在无用的地步,所以我完全绕过它并用我自己的代码创建模式。您可以使用 syncdb 创建数据库,然后直接更改表,例如。 ALTER TABLE tablename DROP CONSTRAINT fk_constraint_name
.
当然,您也会失去 ON DELETE CASCADE 和所有参照完整性检查。
【讨论】:
这行得通!只需要进入数据库并手动处理它。知道如何通过南迁来做到这一点吗? 我在下面发布了一个答案,说明如何使用 South 完成此任务(评论太混乱了)。【参考方案6】:我是 Django 的新手,所以我现在不知道它是否提供了你想要的开箱即用的东西。我想到了这样的事情:
from django.db import models
class YourModel(models.Model):
my_fk = models.PositiveIntegerField()
def set_fk_obj(self, obj):
my_fk = obj.id
def get_fk_obj(self):
if my_fk == None:
return None
try:
obj = YourFkModel.objects.get(pk = self.my_fk)
return obj
except YourFkModel.DoesNotExist:
return None
我不知道您是否使用 contrib 管理应用程序。使用 PositiveIntegerField 而不是 ForeignKey 该字段将在管理站点上以文本字段呈现。
【讨论】:
这样的东西是我迄今为止想出的最好的东西。我希望有更多,因为这样我们就失去了一堆很酷的 django 对象关系的东西。 您可以同时拥有一个 django 外键。如果有外键,请使用它,如果没有 - 获取 PositiveIntegerField 中的数据。以上是关于不需要参照完整性的Django ForeignKey?的主要内容,如果未能解决你的问题,请参考以下文章