Django:'unique_together'和'blank = True'

Wes*_*Dec 22 python sql django

我有一个看起来像这样的Django模型:

class MyModel(models.Model):
    parent = models.ForeignKey(ParentModel)
    name   = models.CharField(blank=True, max_length=200)
    ... other fields ...

    class Meta:
        unique_together = ("name", "parent")
Run Code Online (Sandbox Code Playgroud)

这按预期工作; 如果同时存在name多次,parent则会出现错误:"具有此Name和Parent的MyModel已存在."

但是,当我保存多个MyModel相同parentname字段为空白时,我也会收到错误,但是应该允许这样做.所以基本上我不想在name字段为空时得到上述错误.这有可能吗?

rom*_*rcz 16

首先,空白(空字符串)与null('' != None)不同.

其次,当你将字段留空时,通过表单使用的Django CharField将存储空字符串.

因此,如果您的字段不是CharField,那么您应该添加null=True它.但在这种情况下,你需要做的不仅仅是这些.您需要创建子类forms.CharField并覆盖它的clean方法以在空字符串上返回None,如下所示:

class NullCharField(forms.CharField):
    def clean(self, value):
        value = super(NullCharField, self).clean(value)
        if value in forms.fields.EMPTY_VALUES:
            return None
        return value
Run Code Online (Sandbox Code Playgroud)

然后在ModelForm的表单中使用它:

class MyModelForm(forms.ModelForm):
    name = NullCharField(required=False, ...)
Run Code Online (Sandbox Code Playgroud)

这样,如果你把它留空,它将在数据库中存储null而不是空字符串('')

  • +1我更喜欢具有数据库强制约束的解决方案. (2认同)

Mat*_*ell 11

使用unique_together,你告诉Django你不希望任何两个MyModel具有相同parentname属性的实例- 即使name是一个空字符串也适用.

这是使用unique相应数据库列上的属性在数据库级别强制执行的.因此,要对此行为进行任何例外处理,您必须避免unique_together在模型中使用.

相反,您可以通过覆盖save模型上的方法并在那里强制执行唯一约束来获得您想要的结果.当您尝试保存模型的实例时,您的代码可以检查是否存在具有相同parentname组合的任何现有实例,并且如果存在则拒绝保存实例.但是如果name是空字符串,您也可以允许保存实例.这个的基本版本可能如下所示:

class MyModel(models.Model):
    ...

    def save(self, *args, **kwargs):

        if self.name != '':
            conflicting_instance = MyModel.objects.filter(parent=self.parent, \
                                                          name=self.name)
            if self.id:
                # This instance has already been saved. So we need to filter out
                # this instance from our results.
                conflicting_instance = conflicting_instance.exclude(pk=self.id)

            if conflicting_instance.exists():
                raise Exception('MyModel with this name and parent already exists.')

        super(MyModel, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.

  • 我认为使用.exists是首选方法,而不是len(queryset). (3认同)