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

时间:2015-01-18 16:57:55

标签: django database django-models

如果可选Foreign Key缺少外表中的相关值,我可以:

  1. 将其设为null

  2. 将其指向外表中的空字符串''

  3. 在我看来,如果您遵循Django设计实践,最终会得到选项2(请参阅下面的代码)。这两种方法都有明显的优势或缺点吗?

    Django在设计/惯例方面有点偏爱2。文档说null''是“无数据”的2个可能值。因此,如果省略可选字段,表单将正确验证并提供Foreign Key可指向的空字符串。 但从逻辑上讲,似乎缺失值似乎意味着null或缺少Foreign Key(而不是指向空值的有效Foreign Key)。

    此外,如果我只是列出所有专辑或进行计数,那么存储空白将是一个难题。每次我都记得要避免空字符串。相反,null外键永远不会在专辑表中有条目。

    参考:

    1. Can a foreign key be NULL...
    2. Can foreign key be NULL
    3. Django docs on null vs blank

    4. 代码的可选细节:

      #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)
      

2 个答案:

答案 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_internationalDateField release_date)。现在你必须为你的神奇行中的这些字段组成任意虚假的值,而这些字段并不能代表真实的专辑。

所以我建议坚持传统的选择1。

在表单中处理此内容非常简单。例如,在ModelForms中,ForeignKeyModelChoiceField表示。如果ForeignKeyblank=Truenull=True,则下拉列表中的其中一个选项将为空,并且保存选择了该选项的表单将使相应的数据库字段NULL

在您的情况下,您似乎让用户直接在Song表单上输入相册名称。这完全没问题,但当然你必须有特殊的逻辑来解释这个价值并创造出合适的模型。您上面的示例代码不是黑客,也不是数据验证。验证是否允许空值,并且由blank=True(在模型中)或required=False(在表单中)控制。