проверка формы django на основе того, имеет ли поле значение

  1. Когда я отправляю пустую форму с выбранным phone_type (для formHomePhone), форма возвращает себя без значения, выбранного в phone_type с условием This field is required

  2. Как видно из представления, первый номер телефона в форме обязателен, а остальные нет. Я хочу обрабатывать их только в том случае, если есть значение. Хотя, когда я нажимаю «Отправить» в пустой форме, дополнительные поля номера телефона выдают ошибку из UKPhoneNumberField > Phone number must include an area code.. Как я могу проверить только наличие номера в уважаемом поле?

У меня есть такой файл view.py

def new_client_view(request):
    if request.method == 'POST':
        formDetails = ClientDetailsForm(request.POST)
        formAddress = ClientAddressForm(request.POST)
        formHomePhone = ClientPhoneForm(request.POST)
        formWorkPhone = ClientOtherPhoneForm(request.POST)
        formMobilePhone = ClientOtherPhoneForm(request.POST)
        if formDetails.is_valid() and formAddress.is_valid() and formHomePhone.is_valid():
            c = Client()
            c.save()
            fd = formDetails.save(commit=False)
            fd.client = c
            fd.created_by = request.user
            fd.save()
            fa = formAddress.save(commit=False)
            fa.client = c
            fa.created_by = request.user
            fa.save()
            fph = formHomePhone.save(commit=False)
            fph.client = c
            fph.created_by = request.user
            fph.save()
            if 'p2-number' in request.POST and request.POST['p2-number']:
                fpw = formWorkPhone.save(commit=False)
                fpw.client = c.id
                fpw.created_by = request.user
                if fpw.is_valid():
                    fpw.save()
            if 'p3-number' in request.POST and request.POST['p3-number']:
                fpm = formMobilePhone.save(commit=False)
                fpm.client = c
                fpm.created_by = request.user
                if fpm.is_valid():
                    fpm.save()
            return render_to_response('client/client.html', context_instance=RequestContext(request))
        else:
            return render_to_response('client/new_client.html', {'formDetails': formDetails, 'formAddress': formAddress, 'formHomePhone': formHomePhone, 'formWorkPhone': formWorkPhone, 'formMobilePhone': formMobilePhone}, context_instance=RequestContext(request))


    else:
        formAddress = ClientAddressForm()
        formDetails = ClientDetailsForm()
        formHomePhone = ClientPhoneForm(initial={'phone_type':'home'}, prefix="p1")
        formWorkPhone = ClientPhoneForm(initial={'phone_type':'work'}, prefix="p2")
        formMobilePhone = ClientPhoneForm(initial={'phone_type':'mobi'}, prefix="p3")
        return render_to_response('client/new_client.html', {'formDetails': formDetails, 'formAddress': formAddress, 'formHomePhone': formHomePhone, 'formWorkPhone': formWorkPhone, 'formMobilePhone': formMobilePhone}, context_instance=RequestContext(request))

формы.py, как это:

class ClientDetailsForm(ModelForm):
    class Meta:
        model = ClientDetails
        exclude = ('client', 'created', 'created_by')

class ClientAddressForm(ModelForm):
    class Meta:
        model = ClientAddress
        exclude = ('client', 'created', 'created_by')

class ClientPhoneForm(ModelForm):
    number = UKPhoneNumberField()

    class Meta:
        model = ClientPhone
        exclude = ('client', 'created', 'created_by')

class ClientOtherPhoneForm(ModelForm):
    number = UKPhoneNumberField(required=False)

    class Meta:
        model = ClientPhone
        exclude = ('client', 'created', 'created_by')

и модели.py вроде этого:

MARITAL_STATUS_CHOICES = (
    ...
)

NAME_TITLE_CHOICES = (
    ...
)

PHONE_CHOICES = (
    ('home', 'Home'),
    ('home2', 'Home 2'),
    ('mobi', 'Mobile'),
    ('mobi2', 'Mobile 2'),
    ('work', 'Work'),
    ('work2', 'Work 2'),
)

class Client(models.Model):
    closed = models.DateTimeField(blank=True, null=True)
    closed_by = models.ForeignKey(User, blank=True, null=True)
    comment = models.TextField(blank=True, null=True)

    def __unicode__(self):
        return u'%s' % (self.id)

class ClientDetails(models.Model):
    ...

class ClientAddress(models.Model):
    ...

class ClientPhone(models.Model):
    client = models.ForeignKey(Client, null=True)
    created = models.DateTimeField(default=datetime.now)
    created_by = models.ForeignKey(User, blank=True, null=True)
    phone_type = models.CharField(max_length=5, choices=PHONE_CHOICES)
    number = models.CharField(max_length=24)

Кстати(знаю, что моя функция new_client_view не очень СУХАЯ. Будем признательны за любые рекомендации)


person Robert Johnstone    schedule 18.01.2011    source источник


Ответы (2)


Если вы хотите, чтобы поля ModelForm не проверялись, когда они пусты, задайте пустое = True, null = True в определении модели.

Если вы не хотите менять модель, вы можете переопределить атрибут required в ModelForm (см. ниже), но имейте в виду, что ваш метод form.save() может попытаться оставить нулевые значения там, где БД ожидает не- нули.

class FooForm(forms.ModelForm):

    class Meta:
         #exclude etc as you wish

    def __init__(self, *args, **kwargs):

         #init the form as usual
         super(FooForm, self).__init__(*args, **kwargs)

         #then change the required status on the fields:
         self.fields['baz'].required = False

ОБНОВЛЕНИЕ Здесь совершенно другой подход, учитывая, что комментарии ОП, похоже, подразумевают, что проверка между формами может быть тем, что ему нужно:

Если вы хотите, чтобы одна из ваших текущих форм вела себя определенным образом, основываясь на том, что находится в другой форме, вы говорите о проверке кросс-формы, что невозможно, поскольку методы очистки форм не всегда знают, какие другие формы обрабатываются. этим взглядом.

Вместо этого я бы рекомендовал создать новый класс forms.Form, в котором есть все поля моделей, с которыми вы хотите иметь дело, но который не является ModelForm. Затем вы можете добавить к этой форме метод save() (или process(), или любой другой), который будет выполнять фанданго, которое вы сейчас имеете в своем представлении. Это также может сделать ваш код более аккуратным.

Вам нужно будет передать request.user в форму в качестве аргумента (kwarg проще сделать правильно), чтобы вы могли установить соответствующего пользователя для моделей, которые вы сохраняете.

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

Итак, как быстрый непроверенный код:

from django import forms
from django.forms.util import ErrorList

class ClientForm(forms.Form):

    ## define your fields here ##
    home_phone = forms.CharField(required=False) 
    # etc etc

    def __init__(self, *args, **kwargs):
        """
        NB: in the view you'll need to call the form's constructor with 
        keyword arguments for client (the client whose details are being 
        edited) and user (ie: request.user)
        """

        #before the form is initialised, grab the two extra objects we'll 
        #need in save()

        self.client = kwargs.pop('client') 
        #NB:this will explode if there is no client kwarg present

        self.user = kwargs.pop('user') #ditto, for a 'user' argument

        #now carry on with the standard form initialisation
        super(ClientForm, self).__init__(*args, **kwargs)


    def clean(self):

        #probe your cleaned data here and enforce any cross-field dependencies you wish

        if self.cleaned_data.get('foo') and not self.cleaned_data.get('bar'):
            self._errors['foo'] = ErrorList(["A Foo should only be present if there\'s not a Bar"])
            # NB: only override a value in an _errors dict if you're sure there is 
            # nothing in there already (ie: the foo field has been cleaned and is 
            # present in cleaned_data)
            # Otherwise, you should append() a string to the ErrorList in question

        return self.cleaned_data

    def save(self):

        #what you have in the view is doing a lot of the legwork, so extend that
        #code here to get or create the relevant ClientHomePhone etc models
        #based on lookups related to self.client, which we set in __init__() above

        #also, remember you'll have access to self.user which is the user who is 
        #creating/editing so you can set this for created_by

        return self.client # or some other relevant entity, if you wish
person Steve Jalim    schedule 18.01.2011
comment
Я хотел бы сделать это для каждой формы, а не для каждой модели, если это возможно. - person Robert Johnstone; 18.01.2011
comment
см. мой отредактированный ответ выше. У меня также есть объяснение в моем заднем кармане о перекрестной проверке формы, но я не могу сказать, действительно ли это то, что вы хотите вместо этого. - person Steve Jalim; 18.01.2011
comment
Спасибо за функцию, но я действительно надеялся, что в классе представления › new_client_view будет немного логики, которая ТОЛЬКО выполняла бы проверку и сохраняла номер телефона, если бы в поле number было что-то в первую очередь. - person Robert Johnstone; 18.01.2011
comment
Проверка формы происходит в форме, а не в представлении. Я обновил свой ответ другим, более длинным подходом, который вы можете использовать или не использовать. - person Steve Jalim; 18.01.2011
comment
Спасибо, что нашли время, чтобы объяснить это. Я просто удивлен, что у вас не может быть if data in form: if from.is_valid: form.save() в функции просмотра. Вы не можете проверить данные, если пользователь их не ввел! - person Robert Johnstone; 18.01.2011

Я нашел ответ, который выглядит примерно так (да, я знаю, что это глупо!)

def new_client_view(request):
    if request.method == 'POST':
        formDetails = ClientDetailsForm(request.POST)
        formAddress = ClientAddressForm(request.POST)
        formHomePhone = ClientPhoneForm(request.POST)
        if formDetails.is_valid() and formAddress.is_valid() and formHomePhone.is_valid():
            c = Client()
            c.save()
            fd = formDetails.save(commit=False)
            fd.client = c
            fd.created_by = request.user
            fd.save()
            fa = formAddress.save(commit=False)
            fa.client = c
            fa.created_by = request.user
            fa.save()
            fph = formHomePhone.save(commit=False)
            fph.client = c
            fph.created_by = request.user
            fph.save()
            if 'p2-number' in request.POST and request.POST['p2-number']:
                formWorkPhone = ClientOtherPhoneForm(request.POST)
                fpw = formWorkPhone.save(commit=False)
                fpw.client = c.id
                fpw.created_by = request.user
                # if the work number is valid
                if fpw.is_valid():
                    # check the mobile number before you save the work number
                    if 'p3-number' in request.POST and request.POST['p3-number']:
                        formMobilePhone = ClientOtherPhoneForm(request.POST)
                        fpm = formMobilePhone.save(commit=False)
                        fpm.client = c
                        fpm.created_by = request.user
                        # if the mobile number is good then there is no more checking to do
                        if fpm.is_valid():
                            fpm.save()
                        # else load up the form again with the error for the mobile phone and the original
                        # data for the mobile phone and the work phone
                        else:
                            formWorkPhone = ClientOtherPhoneForm(request.POST)
                            return render_to_response('client/new_client.html', {'formDetails': formDetails, 'formAddress': formAddress, 'formHomePhone': formHomePhone, 'formWorkPhone': formWorkPhone, 'formMobilePhone': formMobilePhone}, context_instance=RequestContext(request))
                    # if the work number is valid and the mobile field wasn't used then just save the work number
                    fpw.save()
                # if the work number is not valid then find out if a mobile number
                # was entered and feed it back to the form
                else:
                    if 'p3-number' in request.POST and request.POST['p3-number']:
                        formMobilePhone = ClientOtherPhoneForm(request.POST)
                    else:
                        formMobilePhone = ClientPhoneForm(initial={'phone_type':'mobi'}, prefix="p3")
                    return render_to_response('client/new_client.html', {'formDetails': formDetails, 'formAddress': formAddress, 'formHomePhone': formHomePhone, 'formWorkPhone': formWorkPhone, 'formMobilePhone': formMobilePhone}, context_instance=RequestContext(request))

            return render_to_response('client/client.html', context_instance=RequestContext(request))
        # if the form doesn't validate then you need to find out if the optional fields contain data,
        # and if so load that data back into the form
        else:
            if 'p2-number' in request.POST and request.POST['p2-number']:
                formWorkPhone = ClientOtherPhoneForm(request.POST, prefix="p2")
            else:
                formWorkPhone = ClientPhoneForm(initial={'phone_type':'work'}, prefix="p2")
            if 'p3-number' in request.POST and request.POST['p3-number']:
                formMobilePhone = ClientOtherPhoneForm(request.POST, prefix="p3")
            else:
                formMobilePhone = ClientPhoneForm(initial={'phone_type':'mobi'}, prefix="p3")

            return render_to_response('client/new_client.html', {'formDetails': formDetails, 'formAddress': formAddress, 'formHomePhone': formHomePhone, 'formWorkPhone': formWorkPhone, 'formMobilePhone': formMobilePhone}, context_instance=RequestContext(request))


    else:
        formAddress = ClientAddressForm()
        formDetails = ClientDetailsForm()
        formHomePhone = ClientPhoneForm(initial={'phone_type':'home'}, prefix="p1")
        formWorkPhone = ClientPhoneForm(initial={'phone_type':'work'}, prefix="p2")
        formMobilePhone = ClientPhoneForm(initial={'phone_type':'mobi'}, prefix="p3")
        return render_to_response('client/new_client.html', {'formDetails': formDetails, 'formAddress': formAddress, 'formHomePhone': formHomePhone, 'formWorkPhone': formWorkPhone, 'formMobilePhone': formMobilePhone}, context_instance=RequestContext(request))

Я думаю, мне нужно перезаписать функцию is_valid в форме, чтобы убедиться, что в одном из полей есть данные, прежде чем делать что-то вроде super.is_valid() (иначе возвращая true, чтобы я не вызывал ошибку для пустой формы), а также функцию pre-save в форме, которая берет объекты user и client, помещает их в поля и проверяет наличие номера телефона перед сохранением.

  1. Перезапишите is_valid(), чтобы убедиться, что пустые значения могут пройти
  2. Создайте pre-save() для загрузки User и Client в модель и прервите, если можете, если нет значения для number

ЭТО БЕЗУМИЕ!!!

Единственная другая вещь, о которой я могу думать, это сделать phone_type и number в модели ClientPhone blank=True, null=True, чтобы у меня были необязательные поля телефона.

ЭТО ТОЖЕ БЕЗУМИЕ!!!

Почему я должен нарушать целостность моей модели только для того, чтобы иметь поле выбора в одной из моих форм???

РЕДАКТИРОВАТЬ:

view.py стал чище, когда я сделал с файлом forms.py следующее:

class ClientDetailsForm(ModelForm):
    class Meta:
        model = ClientDetails
        exclude = ('client', 'created', 'created_by')

    def CustomSave(self,c,u):
        t = self.save(commit=False)
        t.client = c
        t.created_by = u
        t.first_name = self.cleaned_data['first_name'].capitalize()
        t.middle_name = self.cleaned_data['first_name'].capitalize()
        t.last_name = self.cleaned_data['first_name'].capitalize()
        t.save()

class ClientAddressForm(ModelForm):
    class Meta:
        model = ClientAddress
        exclude = ('client', 'created', 'created_by')

    def CustomSave(self,c,u):
        t = self.save(commit=False)
        t.client = c
        t.created_by = u
        t.address_1 = self.cleaned_data['address_1'].capitalize()
        t.address_2 = self.cleaned_data['address_2'].capitalize()
        t.address_3 = self.cleaned_data['address_3'].capitalize()
        t.address_4 = self.cleaned_data['address_4'].capitalize()
        t.post_code = self.cleaned_data['post_code'].upper()
        t.save()

class ClientPhoneForm(ModelForm):
    number = UKPhoneNumberField()

    class Meta:
        model = ClientPhone
        exclude = ('client', 'created', 'created_by')

    def CustomSave(self,c,u):
        t = self.save(commit=False)
        t.client = c
        t.created_by = u
        t.save()

class ClientOtherPhoneForm(ModelForm):
    number = UKPhoneNumberField(required=False)

    class Meta:
        model = ClientPhone
        exclude = ('client', 'created', 'created_by')

    def CustomSave(self,c,u):
        t = self.save(commit=False)
        t.client = c
        t.created_by = u
        t.save()
person Robert Johnstone    schedule 18.01.2011