Могу ли я рассчитывать на порядок проверки полей в форме Джанго? - PullRequest
8 голосов
/ 21 июля 2010

У меня есть форма Django с полем имени пользователя и адреса электронной почты.Я хочу проверить, что электронная почта еще не используется пользователем:

def clean_email(self):
    email = self.cleaned_data["email"]
    if User.objects.filter(email=email).count() != 0:
        raise forms.ValidationError(_("Email not available."))
    return email

Это работает, но вызывает некоторые ложные отрицания, поскольку электронная почта может уже находиться в базе данных для пользователя, указанного в форме.Я хочу изменить на это:

def clean_email(self):
    email = self.cleaned_data["email"]
    username = self.cleaned_data["username"]
    if User.objects.filter(email=email,  username__ne=username).count() != 0:
        raise forms.ValidationError(_("Email not available."))
    return email

Документы Django говорят, что вся проверка для одного поля выполняется перед переходом к следующему полю.Если электронная почта очищается перед именем пользователя, cleaned_data["username"] не будет доступна в clean_email.Но в документах неясно, в каком порядке очищаются поля. Я объявляю имя пользователя перед электронной почтой в форме, означает ли это, что я могу предположить, что имя пользователя очищено перед электронной почтой?

Я мог бы прочитать код, но меня больше интересует то, что обещает API Django, и зная, что я в безопасности даже в будущих версиях Django.

Ответы [ 4 ]

8 голосов
/ 23 марта 2012

Обновление

.keyOrder больше не работает.Я считаю, что это должно работать вместо:

from collections import OrderedDict


class MyForm(forms.ModelForm):
    …

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        field_order = ['has_custom_name', 'name']
        reordered_fields = OrderedDict()
        for fld in field_order:
            reordered_fields[fld] = self.fields[fld]
        for fld, value in self.fields.items():
            if fld not in reordered_fields:
                reordered_fields[fld] = value
        self.fields = reordered_fields

Предыдущий ответ

Существуют вещи, которые могут изменить порядок формы независимо от того, как вы объявите их в определении формы.Один из них - если вы используете ModelForm, и в этом случае, если у вас нет обоих полей, объявленных в fields в class Meta, они будут в непредсказуемом порядке.1014 * есть надежное решение .

Вы можете управлять порядком полей в форме, задав self.fields.keyOrder.

Вот пример кода, который вы можете использовать:

class MyForm(forms.ModelForm):
    has_custom_name = forms.BooleanField(label="Should it have a custom name?")
    name = forms.CharField(required=False, label="Custom name")

    class Meta:
        model = Widget
        fields = ['name', 'description', 'stretchiness', 'egginess']

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        ordered_fields = ['has_custom_name', 'name']
        self.fields.keyOrder = ordered_fields + [k for k in self.fields.keys() if k not in ordered_fields]

    def clean_name(self):
        data = self.cleaned_data
        if data.get('has_custom_name') and not data.get('name'):
            raise forms.ValidationError("You must enter a custom name.")
        return data.get('name')

Если установлено keyOrder, has_custom_name будет проверено (и, следовательно, присутствует в self.cleaned_data) до подтверждения name.

7 голосов
/ 21 июля 2010

Документы Django утверждают, что это в порядке определения поля.

Но я обнаружил, что это не всегда соответствует этому обещанию. Источник: http://docs.djangoproject.com/en/dev/ref/forms/validation/

Эти методы выполняются в порядке приведено выше, одно поле за раз. Тот для каждого поля в форме (в порядок они объявлены в виде определение), метод Field.clean () (или его переопределение) выполняется, затем clean_ (). Наконец, однажды эти два метода запускаются для каждого поле, метод Form.clean () или его переопределить, выполняется.

7 голосов
/ 21 июля 2010

Нет никаких обещаний, что поля обрабатываются в каком-либо конкретном порядке.Официальная рекомендация заключается в том, что любая проверка, которая зависит от нескольких полей, должна выполняться в методе clean() формы, а не в методах clean_foo(), специфичных для поля.

3 голосов
/ 21 июня 2012

Метод подкласса clean ().Этот метод может выполнить любую проверку, которая требует доступа к нескольким полям из формы одновременно.Здесь вы можете указать, что, если поле A указано, поле B должно содержать действительный адрес электронной почты и тому подобное.Данные, которые возвращает этот метод, являются последним атрибутом cleaned_data для формы, поэтому не забудьте вернуть полный список очищенных данных, если вы переопределите этот метод (по умолчанию Form.clean () просто возвращает self.cleaned_data).

Копировать-вставить из https://docs.djangoproject.com/en/dev/ref/forms/validation/#using-validators

Это означает, что если вы хотите проверить, что значения, такие как значение адреса электронной почты и parent_email, не совпадают, вы должны сделать это внутри этой функции,т.е.:

from django import forms

from myapp.models import User

class UserForm(forms.ModelForm):
    parent_email = forms.EmailField(required = True)

    class Meta:
        model = User
        fields = ('email',)

    def clean_email(self):
        # Do whatever validation you want to apply to this field.
        email = self.cleaned_data['email']
        #... validate and raise a forms.ValidationError Exception if there is any error
        return email

    def clean_parent_email(self):
        # Do the all the validations and operations that you want to apply to the
        # the parent email. i.e: Check that the parent email has not been used 
        # by another user before.
        parent_email = self.cleaned_data['parent_email']
        if User.objects.filter(parent_email).count() > 0:
            raise forms.ValidationError('Another user is already using this parent email')
        return parent_email

    def clean(self):
        # Here I recommend to user self.cleaned_data.get(...) to get the values 
        # instead of self.cleaned_data[...] because if the clean_email, or 
        # clean_parent_email raise and Exception this value is not going to be 
        # inside the self.cleaned_data dictionary.

        email = self.cleaned_data.get('email', '')
        parent_email = self.cleaned_data.get('parent_email', '')
        if email and parent_email and email == parent_email:
            raise forms.ValidationError('Email and parent email can not be the same')
        return self.cleaned_data
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...