簡體   English   中英

Django:'unique_together'和'blank = True'

[英]Django: 'unique_together' and 'blank=True'

我有一個看起來像這樣的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")

這按預期工作; 如果有相同name比同一次parent然后我得到一個錯誤:“為MyModel這個名稱和家長已經存在”

但是,當我使用相同的parent保存多個MyModelname字段為空時,我也會收到錯誤,但是應該允許這樣做。 所以基本上我不想在name字段為空時得到上述錯誤。 這有可能嗎?

首先,空白(空字符串)與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

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

class MyModelForm(forms.ModelForm):
    name = NullCharField(required=False, ...)

這樣,如果你把它留空,它將在數據庫中存儲null而不是空字符串( ''

使用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)

希望有所幫助。

這個解決方案與@bigmattyh給出的解決方案非常相似,但是,我發現下面的頁面描述了應該在哪里進行驗證:

http://docs.djangoproject.com/en/1.3/ref/models/instances/#validating-objects

我最終使用的解決方案如下:

from django    import forms

class MyModel(models.Model):
...

def clean(self):
    if self.name != '':
        instance_exists = MyModel.objects.filter(parent=self.parent,
                                                 name=self.name).exists()
        if instance_exists:
            raise forms.ValidationError('MyModel with this name and parent already exists.')

請注意,引發了ValidationError而不是一般異常。 此解決方案的好處是,在使用.is_valid()驗證ModelForm時,會自動調用上面的模型.clean()方法,並將ValidationError字符串保存在.errors中,以便它可以顯示在html模板中。

如果您不同意此解決方案,請與我們聯系。

bigmattyh對發生的事情給出了很好的解釋。 我只想添加一個可能的save方法。

def save(self, *args, **kwargs):
    if self.parent != None and MyModels.objects.filter(parent=self.parent, name=self.name).exists():
        raise Exception('MyModel with this name and parent exists.')
    super(MyModel, self).save(*args, **kwargs)

我認為我選擇通過覆蓋我的模型的干凈方法來做類似的事情,它看起來像這樣:

from django.core.exceptions import ValidationError
def clean(self):
    if self.parent != None and MyModels.objects.filter(parent=self.parent, name=self.name).exists():
        raise ValidationError('MyModel with this name and parent exists.')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM