Django 可选外键应该为NULL还是指向空字符串
当可选的Django 可选外键应该为NULL还是指向空字符串,django,database,django-models,Django,Database,Django Models,当可选的外键缺少外部表中的相关值时,我可以: 将其设置为null 将其指向外部表中的空字符串' 在我看来,如果您遵循Django设计实践,您最终会得到选项2(请参见下面的代码)。这两种方法都有明显的优点或缺点吗 Django在某种程度上出于设计/惯例偏爱2。文档说明null和'是两个可能的“无数据”值。因此,如果省略可选字段,表单将正确验证并提供一个空字符串,您的外键可以指向该字符串。 然而,从逻辑上看,缺少的值似乎意味着null或缺少外键(而不是指向空值的有效外键) 此外,如果我只是列出所有专
外键
缺少外部表中的相关值时,我可以:
null
'
2
(请参见下面的代码)。这两种方法都有明显的优点或缺点吗
Django在某种程度上出于设计/惯例偏爱2
。文档说明null
和'
是两个可能的“无数据”值。因此,如果省略可选字段,表单将正确验证并提供一个空字符串,您的外键可以指向该字符串。
然而,从逻辑上看,缺少的值似乎意味着null
或缺少外键
(而不是指向空值的有效外键
)
此外,如果我只是列出所有专辑或进行计数,存储空白将是一个难题。每次我都要记得避免使用空字符串。相反,null
外键在相册表中永远不会有条目
参考:
Django docs on
带代码的可选详细信息:
#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':'''}
。这将在相册
表中创建一个名称为空的条目。我可以在视图中绕过这一点(参见下面的代码)。但这似乎是一种黑客行为,因为数据验证应该在表单中完成
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)
仔细考虑后,与方法1
相比,方法2
(缺少外部关系意味着对空字符串使用FK
)有一个优势
使用方法2
将更容易在(song.name,song.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
Missing album=>FK为空Missing album=>FK指向空名称
歌曲|专辑歌曲|专辑
---------------- ----------------
“a”|“x”a”|“x”
“b”|“y”“b”|“y”
‘c’|‘y’‘c’|‘y’
“d'| null'd'|”
“d”| nullDjango的惯例是使用'
表示基于文本的字段中缺少数据。然而,对于ForeignKeys
,Django和其他地方的惯例是通过NULL
(即选项1)表示缺少数据
您的选项2混淆了没有名字的专辑和没有专辑的歌曲之间的区别。Django的约定涉及不同的方式来表示允许的字段中的数据缺乏。但在您的情况下,没有名字的相册是无效的,因此选项2需要发明一个无效的相册,只是为了给其他型号提供一些参考
您可以这样写:“将其指向外部表中的空字符串”
。但请注意,外键并不指向外部表中的字段,而是指向整行。假设您在相册
模型中有其他字段(例如,一个布尔字段
是国际的
,或者一个日期字段
发布日期
)。现在,你必须在你神奇的行中为这些字段编造任意的假值,这些字段并不代表一张真正的专辑
因此,我建议坚持传统的选项1
在表单中处理这个问题是内置的,而且非常简单。例如,在ModelForms
中,ForeignKey
由modelcoocefield
表示。如果ForeignKey
具有blank=True
和null=True
,则下拉列表中的一个选项将为空,并且使用该选项保存表单将使相应的数据库字段null
在您的情况下,看起来您让用户直接在歌曲
表单上输入唱片集名称。这很好,但当然你必须有特殊的逻辑来解释这个值并创建合适的模型。上面的示例代码不是黑客攻击,也不是数据验证。验证是否允许空值,这由blank=True
(在模型中)或required=False
(在表单中)控制。您能否引用一个文档/示例,或在上面的代码中显示,Django如何通过null
在表单中进行验证来表示FK
。关于2
,我原则上同意你的观点,但如果我仅仅根据Django文档进行论证,我会说表示“无数据”Django约定是使用空字符串,而不是NULL
。按照这种逻辑,“no name”不是无效的唱片集名称,而是表示缺少数据。最后,通过“指向空字符串”,我知道FK
指向一行,而不是一个字段。另请参见下面我的回答。您引用的约定与基于文本的字段相关(“避免在基于字符串的字段上使用null,例如CharField
和TextField
…”)。与通常规则不同的是,NULL
表示字段中缺少数据。包括外键
。我编辑了答案以回应您对表单的评论。如果你有一个特定的