如何验证/ clean()一个unique = True字段,而不使用ModelForm? [英] How to validate/clean() a unique=True field without using a ModelForm?

查看:440
本文介绍了如何验证/ clean()一个unique = True字段,而不使用ModelForm?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在自定义表单中,如何验证模型的字段的唯一性(即, unique = True set)?


$ b $我知道django的ModelForm会自动执行一个 validate_unique()函数,该函数在BaseModelForm的 clean()方法 - 所以,当使用ModelForm时,这将被正确处理(如在Admin中)。



然而,我从头开始创建我自己的表单,想知道我该怎么办呢?我认为我最大的障碍是知道清理数据时哪个对象附加在表单上...



一些代码:

  class UserProfile(CreatedModifiedModel):
user = models.ForeignKey(User,unique = True)
display_name = models.CharField('Display名称',max_length = 30,
blank = True,unique = True)

class EditUserProfileForm(forms.Form):
display_name = forms.CharField(required = False,max_length = 30)

#通知是从不同的模型创建的,而不是UserProfile
通知= forms.MultipleChoiceField(
标签=电子邮件通知,
必需= False,
widget = forms.CheckboxSelectMultiple,)

def clean_display_name(self):
#如何在此表单上运行我自己的validate_unique()?
#我如何知道我正在使用哪个UserProfile对象?

#更多的代码如下,包括__init__设置通知


解决方案

唯一的验证很难完全正确,所以建议使用ModelForm:

  class EditUserProfileForm(forms.ModelForm):
#通知是从不同的模型创建的,而不是UserProfile
notifications = forms.MultipleChoiceField(
label =电子邮件通知,
required = False,
widget = forms.CheckboxSelectMultiple,)

class Meta:
model = UserProfile
fields =('display_name',)

从多个模型制作表单并不容易,但在这种情况下,您只需添加通知字段到ModelForm,并像往常一样将它从 .cleaned_data 中取出:

 #vi ew 
如果request.method =='POST':
form = EditUserProfileForm(request.POST,instance = user_profile)
如果form.is_valid():
user_profile = form。 save()
notifications = form.cleaned_data ['notifications']
#执行某些通知。

这是我该怎么做,但是如果你设置验证自己的独特性,你可以永远执行以下操作:

  def clean_display_name(self):
display_name = self.cleaned_data ['display_name']
如果UserProfile.objects.filter(display_name = display_name).count()> 0:
raise ValidationError('此显示名称已被使用')
返回display_name

这里有两个问题。首先,您可以遇到并发问题,其中两个人提交相同的名称,都会通过唯一的检查,但是会出现DB错误。另一个问题是您无法编辑用户个人资料,因为您没有从搜索中排除的ID。您必须将其存储在您的 __ init __ 中,然后在清洁中使用:

 code> def __init __(self,* args,** kwargs):
...
如果实例在kwargs中:
self.id = kwargs ['instance'] .id
...

def clean_display_name(self):
display_name = self.cleaned_data ['display_name']
qs = UserProfile.objects.filter(display_name = display_name)
如果self.id:
qs = qs.exclude(pk = self.id)
如果qs.count()> 0:
raise ValidationError('此显示名称已被使用')
返回display_name

但是在这一点上,你只是在ModelForms中复制逻辑。


In a custom Form, how does one validate a Model's field's uniqueness (i.e., has unique=True set)?

I know that django's ModelForm automatically performs a validate_unique() function that is called within the BaseModelForm's clean() method -- so, when using ModelForm, this will be handled correctly (as it is in the Admin).

However, I am creating my own form from scratch and wonder how I can go about handling this myself? I think my biggest stumbling block is knowing which object is attached to the form when the data is being cleaned ...

Some code:

class UserProfile(CreatedModifiedModel):
    user            = models.ForeignKey(User, unique=True)
    display_name    = models.CharField('Display Name',max_length=30,
                        blank=True,unique=True)

class EditUserProfileForm(forms.Form):
    display_name    = forms.CharField(required=False,max_length=30)

    # "notifications" are created from a different model, not the UserProfile
    notifications    = forms.MultipleChoiceField(
                        label="Email Notifications",
                        required=False,
                        widget=forms.CheckboxSelectMultiple,)

    def clean_display_name(self):
        # how do I run my own validate_unique() on this form?
        # how do I know which UserProfile object I am working with?

    # more code follows, including the __init__ which sets up the notifications

解决方案

Unique validation is hard to get completely right, so I would recommend using a ModelForm anyways:

class EditUserProfileForm(forms.ModelForm):
    # "notifications" are created from a different model, not the UserProfile
    notifications    = forms.MultipleChoiceField(
                        label="Email Notifications",
                        required=False,
                        widget=forms.CheckboxSelectMultiple,)

    class Meta:
        model = UserProfile
        fields = ('display_name',)

Making a form from multiple models is not easy, but in this case you can just add the notifications field onto the ModelForm and pull it out of .cleaned_data as usual:

# view
if request.method == 'POST':
    form = EditUserProfileForm(request.POST, instance=user_profile)
    if form.is_valid():
        user_profile = form.save()
        notifications = form.cleaned_data['notifications']
        # Do something with notifications.

That's how I would do it, but if you're set on validating unique yourself, you can always do something like:

def clean_display_name(self):
    display_name = self.cleaned_data['display_name']
    if UserProfile.objects.filter(display_name=display_name).count() > 0:
        raise ValidationError('This display name is already in use.')
    return display_name

There are two problems I see here. First, you can run into concurrency issues, where two people submit the same name, both pass unique checks, but then one gets a DB error. The other problem is that you can't edit a user profile because you don't have an ID to exclude from the search. You'd have to store it in your __init__ and then use it in the cleaning:

def __init__(self, *args, **kwargs):
    ...
    if 'instance' in kwargs:
        self.id = kwargs['instance'].id
    ...

def clean_display_name(self):
    display_name = self.cleaned_data['display_name']
    qs = UserProfile.objects.filter(display_name=display_name)
    if self.id:
        qs = qs.exclude(pk=self.id)
    if qs.count() > 0:
        raise ValidationError('This display name is already in use.')
    return display_name

But at that point you're just duplicating the logic in ModelForms.

这篇关于如何验证/ clean()一个unique = True字段,而不使用ModelForm?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆