Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/87.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Django:';独特的"在一起"';和';空白=真';_Python_Sql_Django - Fatal编程技术网

Python Django:';独特的"在一起"';和';空白=真';

Python Django:';独特的"在一起"';和';空白=真';,python,sql,django,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") class MyMo

我有一个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")
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)
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.')
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
这是意料之中的事;如果在同一
父项中有相同的
名称
不止一次,那么我会得到一个错误:“具有此名称和父项的MyModel已经存在。”


但是,当我使用相同的
parent
保存多个
MyModel
name
字段为空时,也会出现错误,但这应该是允许的。所以基本上我不想在
name
字段为空时出现上述错误。这有可能吗?

同时使用
unique\u
,您告诉Django您不希望任何两个
MyModel
实例具有相同的
父属性和
name
属性,即使
name
是空字符串,这也适用

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

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

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

    class Meta:
        unique_together = ("name", "parent")
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)
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.')
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

希望这能有所帮助。

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)
我想我选择了通过覆盖我的模型的clean方法来做类似的事情,它看起来像这样:

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

    class Meta:
        unique_together = ("name", "parent")
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)
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.')
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
首先,空白(空字符串)与null不同(
“”!=None

其次,Django CharField在表单中使用时,当您将字段留空时,将存储空字符串

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

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

    class Meta:
        unique_together = ("name", "parent")
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)
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.')
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
然后在模型表单中使用它:

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

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

此解决方案与@bigmattyh给出的解决方案非常相似,但是,我发现下面的页面描述了应在何处进行验证:

我最终使用的解决方案如下:

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时,会自动调用上面的models.clean()方法,并将ValidationError字符串保存在.errors中,以便在html模板中显示


如果您不同意此解决方案,请告诉我

我认为使用.exists是优于len(queryset)的首选方法。@bigmatty:这种解决方案并不好,因为它意味着如果您以后要更改实例的其他字段并使用.save()保存它们,然后您将得到一个异常。@WesDec:只有当您试图将一个实例与另一个对象的父对象和名称相同时,才应该得到一个异常,当然,除非名称属性是空字符串。这就是你说的你想要的行为,不是吗?@bigmattyh:是的,但是想象一下,我创建了一个实例,其中“parent”和“name”是唯一的,并且名称不是空字符串。如果我以后从数据库中获取相同的实例,并更改一个字段,然后将该实例保存到数据库中,将再次对“parent”和“name”进行相同的检查,这次我将得到一个异常。@WesDec:这并不难修复。请参阅上面处理此情况的已编辑代码。之所以会看到行为上的差异,是因为您的名称字段是CharField。CharFields不存储NULL,而是存储空字符串。一个快速的谷歌搜索将引导你找到这是为什么。好电话。整个问题实际上是由于charfield+1上缺少null造成的。我更喜欢一个具有数据库强制约束的解决方案。您不应该继承
模型。charfield
而不是
表单。charfield
?不,我改变行为的不是模型字段,而是表单字段。它是将空字段转换为空字符串“”的表单,这非常好,但在唯一性的情况下,None值(数据库中为Null)更合适。此解决方案的唯一问题是,在调用模型的
save()
方法时,不会调用
clean()
(请参阅)。但是我一直不明白为什么,因为它看起来确实像一个更干净(没有双关语)的解决方案。你可以在
模型的
保存
方法中调用
self.clean()