可选的外键应该是 NULL 还是应该指向一个空字符串

Should optional Foreign Key be NULL or should it point to an empty string

当可选的 Foreign Key 缺少外国 table 中的相关值时,我可以:

  1. 设置为null

  2. 指向一个空字符串''在国外table

在我看来,如果您遵循 Django 设计实践,您最终会选择 2(请参阅下面的代码)。这两种方法是否有任何明显的优点或缺点?

Django design/convention 在某种程度上偏爱 2。文档说 null'' 是 "no-data" 的两个可能值。因此,如果您省略可选字段,表单将正确验证并提供您的 Foreign Key 可以指向的空字符串。 然而从逻辑上讲,缺失值似乎应该暗示 null 或缺失 Foreign Key(而不是指向空值的有效 Foreign Key)。

此外,如果我简单地列出所有专辑或进行计数,存储空白将是一个陷阱。每次我都要记住要避免空字符串。相反,null 外键永远不会在相册 table.

中有条目

参考:

  1. Can a foreign key be NULL...
  2. Can foreign key be NULL
  3. null vs blank
  4. 上的 Django 文档

带有代码的可选详细信息:

#models.py
class Album(Model):
    name = CharField(max_length=50, unique=True)

class Song(Model):
    name = CharField(max_length=50) 
    album = ForeignKey(Album, null=True, blank=True)    

#forms.py
class SongForm(Form):
    name = CharField(max_length=50)
    album = CharField(max_length=50, required=False)

如果您有一首没有专辑名称的歌曲,格式为 returns {'name':'foo', 'album':''}。这会在 Album table 中创建一个名称为空的条目。我可以在视图中绕过它(请参见下面的代码)。但这似乎是一种 hack,因为数据验证应该以表单的形式进行。

if album:
    Song.objects.create(name=form.cleaned_data['name'], album=form.cleaned_data['album'])
else:
    Song.objects.create(name=form.cleaned_data['name'], album_id=None)

经过深思熟虑,方法2(缺少外部关系意味着 FK 到空字符串的方法)与方法 1 相比有一个优势。

使用方法 2 可以更轻松地在 (song.name, song.album) 上创建 unique_together 索引。用一个例子可以更好地解释这一点。当空字符串用于 Album 时,Song(下面的 d)的两个相似值将被唯一约束捕获。但是,null 在数据库中被视为不同的值,并且它们不会在情况 1 中被捕获(必须依赖条件索引才能使其工作)。

我不确定这是设计使然还是 Django 约定的偶然优势。

Missing album => FK is null                    Missing Album => FK points to blank name
Song  |   Album                                Song  |   Album
----------------                               ----------------
'a'   |    'x'                                 'a'   |    'x' 
'b'   |    'y'                                 'b'   |    'y'
'c'   |    'y'                                 'c'   |    'y'
'd'   |    null                                'd'   |    ''
'd'   |    null  <- Dup. not caught by DB      'd'   |    ''   <- Duplicate caught by DB

Django 的约定是使用'' 来表示基于文本的字段中缺少数据。然而,当谈到 ForeignKeys 时,Django 和其他地方的惯例是用 NULL(即选项 1)来表示缺少数据。

您的选项 2 混淆了没有名称的专辑没有专辑的歌曲之间的区别。 Django 的约定必须用不同的方式来表示允许的字段中缺少数据。但在您的情况下,无效 没有名称的相册,因此选项 2 需要发明一个无效的相册只是为了给其他模型一些指向。

你写:"Point it to an empty string '' in the foreign table"。但是请注意,外键不指向外部 table 中的字段,它们指向整行。假设您在 Album 模型中有其他字段(例如 BooleanField is_international,或 DateField release_date)。现在您必须在不代表实际相册的神奇行中为这些字段编造任意的假值。

所以我建议坚持使用常规选项 1。

在表单中处理此问题是内置且直接的。例如,在ModelForms中,ForeignKey表示为ModelChoiceField。如果 ForeignKeyblank=Truenull=True,则下拉列表中的一个选项将为空白,保存选择该选项的表单将使相应的数据库字段成为 NULL .

在您的情况下,您似乎让用户直接在 Song 表单上输入相册名称。这很好,但当然您将必须有特殊的逻辑来解释该值并创建适当的模型。您上面的示例代码不是黑客攻击,也不是数据验证。验证是是否允许空值,这由[​​=23=](在模型中)或required=False(在表单中)控制。