如果可选Foreign Key
缺少外表中的相关值,我可以:
将其设为null
将其指向外表中的空字符串''
在我看来,如果您遵循Django设计实践,最终会得到选项2
(请参阅下面的代码)。这两种方法都有明显的优势或缺点吗?
Django在设计/惯例方面有点偏爱2
。文档说null
和''
是“无数据”的2个可能值。因此,如果省略可选字段,表单将正确验证并提供Foreign Key
可指向的空字符串。
但从逻辑上讲,似乎缺失值似乎意味着null
或缺少Foreign Key
(而不是指向空值的有效Foreign Key
)。
此外,如果我只是列出所有专辑或进行计数,那么存储空白将是一个难题。每次我都记得要避免空字符串。相反,null
外键永远不会在专辑表中有条目。
参考:
代码的可选细节:
#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)
如果您的歌曲没有专辑名称,则表单会返回{'name':'foo', 'album':''}
。这将在Album
表中创建一个空白名称的条目。我可以在视图中绕过它(见下面的代码)。但这似乎是一种黑客行为,因为数据验证应该以表格形式完成。
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)
答案 0 :(得分:1)
在考虑更多之后,与方法2
相比,方法FK
(缺少外来关系意味着1
为空字符串的方法)具有一个优势。
使用方法2
可以更轻松地在unique_together
上建立(song.name, song.album)
索引。用一个例子可以更好地解释这一点。当空字符串用于Album
时,两个类似的Song
(下面的d
)值将被唯一约束捕获。但是,null
在DB中被视为不同的值,并且在案例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
答案 1 :(得分:1)
Django的惯例是使用''
来表示基于文本的字段中缺少数据。然而,当谈到ForeignKeys
时,Django和其他地方的惯例是NULL
表示缺少数据(即选项1)。
您的选项2会混淆没有名称的专辑和没有专辑的歌曲之间的区别。 Django的约定必须采用不同的方式来表示允许的字段中缺少数据。但在你的情况下,没有名字的专辑不是有效的,所以选项2需要发明一个无效的专辑,只是为了给其他模特指出一些东西。
您写道:&#34;将其指向外表中的空字符串''
&#34;。但请注意,外键不指向外表中的字段,它们指向整行。想象一下,您在Album
模型中有其他字段(例如,BooleanField
is_international
或DateField
release_date
)。现在你必须为你的神奇行中的这些字段组成任意虚假的值,而这些字段并不能代表真实的专辑。
所以我建议坚持传统的选择1。
在表单中处理此内容非常简单。例如,在ModelForms
中,ForeignKey
由ModelChoiceField
表示。如果ForeignKey
有blank=True
和null=True
,则下拉列表中的其中一个选项将为空,并且保存选择了该选项的表单将使相应的数据库字段NULL
在您的情况下,您似乎让用户直接在Song
表单上输入相册名称。这完全没问题,但当然你必须有特殊的逻辑来解释这个价值并创造出合适的模型。您上面的示例代码不是黑客,也不是数据验证。验证是否允许空值,并且由blank=True
(在模型中)或required=False
(在表单中)控制。