【问题标题】:How to validate/clean() a unique=True field without using a ModelForm?如何在不使用 ModelForm 的情况下验证/清理()唯一 = True 字段?
【发布时间】:2009-10-12 23:23:50
【问题描述】:

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

我知道 django 的 ModelForm 会自动执行 validate_unique() 函数,该函数在 BaseModelForm 的 clean() 方法中调用——因此,在使用 ModelForm 时,这将得到正确处理(就像在 Admin 中一样)。

但是,我正在从头开始创建自己的表单,并且想知道如何自己处理这个问题?我认为我最大的绊脚石是在清理数据时知道哪个对象附加到表单...

一些代码:

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

【问题讨论】:

  • 您是否有任何理由制作自定义表单而不是 ModelForm?
  • 我更新了代码以显示我需要的“通知”字段来自不同的应用程序,但在同一个 EditUserProfileForm 上处理......希望这是有道理的。我不认为我可以从多个模型来源制作 ModelForm ...

标签: django django-forms


【解决方案1】:

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

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',)

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

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

我就是这样做的,但是如果您打算自己验证唯一性,您可以随时执行以下操作:

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

我在这里看到了两个问题。首先,您可能会遇到并发问题,即两个人提交相同的名称,都通过了唯一检查,但随后一个人收到了数据库错误。另一个问题是您无法编辑用户配置文件,因为您没有要从搜索中排除的 ID。您必须将其存储在您的 __init__ 中,然后在清洁时使用它:

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

但此时您只是在复制 ModelForms 中的逻辑。

【讨论】:

  • 嘿-谢谢。我不确定是否将字段添加到与模型不对应的 ModelForm ......但我会尝试一下。如果我有问题会报告。
【解决方案2】:

工作示例!

我使用电子邮件作为唯一字段

在 models.py 中使用下面的代码

User._meta.get_field('email')._unique = True

在 forms.py 中使用下面的代码

class ProfileForm(forms.ModelForm):
    email = forms.EmailField(max_length=255, required=True)

    def clean_email(self):
        user_id = self.cleaned_data['user'].id
        email = self.cleaned_data['email']
        obj = User.objects.filter(email=email).exclude(id = user_id)
        if obj:
            raise forms.ValidationError('User with this email is already exists.Try a new email')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多