6

I'm trying to clean data in using clean method in forms, but when I did it then I dont see validation errors in my form its only error page from django.

I have edit and create form and I would like to have this clean in one place but not copy to 2 views.

Can you give me some advice? I read docs about clean and valid in forms but I have still problems with that. I give example of it:

Views.py

@user_passes_test(lambda u: u.is_staff, login_url='/account/login/')
def client_create(request):

    dict = {}

    if request.method == 'POST':
        form_user = ClientUserCreateForm(request.POST, prefix="user")
        form_client = ClientForm(request.POST, prefix="client")
        if form_user.is_valid() and form_client.is_valid():
            obj_user = form_user.save(commit=False)
            obj_client = form_client.save(commit=False)

            obj_user.username = form_client.cleaned_data['vat']
            obj_user.set_password(form_client.cleaned_data['vat'])
            obj_user.is_active = 1
            obj_user.save()

            obj_client.id_id = obj_user.id
            obj_client.save()

                # Redirect to Client profile
                return HttpResponseRedirect(reverse('client_profile', args={obj_client.id_id}))

        # If forms have error show view again with errors
        dict['form_user'] = form_user
        dict['form_client'] = form_client
        return render(request, 'panel/client/form.html', dict)
    else:
        dict['form_user'] = ClientUserCreateForm(prefix="user")
        dict['form_client'] = ClientForm(prefix="client")
        return render(request, 'panel/client/form.html', dict)

Forms.py

class ClientUserCreateForm(forms.ModelForm):
    first_name = forms.CharField(required=True)
    last_name = forms.CharField(required=True)
    email = forms.CharField(required=True)

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

    def __init__(self, *args, **kwargs):
        super(ClientUserCreateForm, self).__init__(*args, **kwargs)
        self.fields['first_name'].widget.attrs.update({
            'type': 'text',
            'class': 'form-control',
            'id': 'input-text',
        })
        self.fields['last_name'].widget.attrs.update({
            'type': 'text',
            'class': 'form-control',
            'id': 'input-text',
        })
        self.fields['email'].widget.attrs.update({
            'type': 'text',
            'class': 'form-control',
            'id': 'input-text',
        })

    def clean(self):
        data = self.cleaned_data

        first_name = data.get('first_name')
        last_name = data.get('last_name')
        email = data.get('email')

        data['first_name'] = first_name[0].upper() + first_name[1:].lower()
        data['last_name'] = last_name[0].upper() + last_name[1:].lower()
        data['email'] = email.lower()

        return data


class ClientForm(forms.ModelForm):
    tags = forms.CharField(widget=forms.Textarea, required=False)

    class Meta:
        model = Client
        fields = ('address', 'zip_code', 'city', 'country', 'forwarding_address',
                  'forwarding_zip_code', 'forwarding_city', 'forwarding_country',)

    def __init__(self, *args, **kwargs):
        super(ClientForm, self).__init__(*args, **kwargs)
        self.fields['country'].queryset = CountriesChoices.objects.all()
        self.fields['forwarding_country'].queryset = CountriesChoices.objects.all()
        self.fields['country'].initial = 1
        self.fields['address'].widget.attrs.update({
            'type': 'text',
            'class': 'form-control',
            'id': 'input-text',
        })
        self.fields['zip_code'].widget.attrs.update({
            'type': 'text',
            'class': 'form-control',
            'id': 'input-text',
        })
        self.fields['city'].widget.attrs.update({
            'type': 'text',
            'class': 'form-control',
            'id': 'input-text',
        })
        self.fields['country'].widget.attrs.update({
            'class': 'form-control',
        })


    def clean(self):
        data = self.cleaned_data

        address = data.get('address')
        zip_code = data.get('zip_code')
        city = data.get('city')
        forwarding_address = data.get('forwarding_address')
        forwarding_zip_code = data.get('forwarding_zip_code')
        forwarding_city = data.get('forwarding_city')

        data['address'] = address[0].upper() + address[1:].lower()
        data['zip_code'] = zip_code
        data['city'] = city[0].upper() + city[1:].lower()

        if len(forwarding_address) > 0:
            data['forwarding_address'] = forwarding_address[0].upper() + forwarding_address[1:].lower()
        else:
            data['forwarding_address'] = address[0].upper() + address[1:].lower()

        if len(forwarding_zip_code) > 0:
            data['forwarding_zip_code'] = forwarding_zip_code
        else:
            data['forwarding_zip_code'] = zip_code

        if len(forwarding_city) > 0:
            data['forwarding_city'] = forwarding_city[0].upper() + forwarding_city[1:].lower()
        else:
            data['forwarding_city'] = city[0].upper() + city[1:].lower()

        return data

For example i leave field "first_name" empty and then i got error django page with: 'NoneType' object is not subscriptable(traceback below) except form page with error "This field is required"(when i comment clean method).

Traceback

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/panel/client/edit/6/

Django Version: 1.8.8
Python Version: 3.5.1
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'core',
 'api',
 'client',
 'registration',
 'avatar',
 'filer',
 'mptt',
 'easy_thumbnails',
 'reversion')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware')


Traceback:
File "C:\Users\loc\dJangoEnvironment\lib\site-packages\django\core\handlers\base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\loc\dJangoEnvironment\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "C:\Users\loc\PycharmProjects\pro\core\views.py" in client_edit
  192.         if form_user.is_valid() and form_client.is_valid():
File "C:\Users\loc\dJangoEnvironment\lib\site-packages\django\forms\forms.py" in is_valid
  184.         return self.is_bound and not self.errors
File "C:\Users\loc\dJangoEnvironment\lib\site-packages\django\forms\forms.py" in errors
  176.             self.full_clean()
File "C:\Users\loc\dJangoEnvironment\lib\site-packages\django\forms\forms.py" in full_clean
  393.         self._clean_form()
File "C:\Users\loc\dJangoEnvironment\lib\site-packages\django\forms\forms.py" in _clean_form
  417.             cleaned_data = self.clean()
File "C:\Users\loc\PycharmProjects\pro\core\forms.py" in clean
  421.         data['first_name'] = first_name[0].upper() + first_name[1:].lower()

Exception Type: TypeError at /panel/client/edit/6/
Exception Value: 'NoneType' object is not subscriptable
3
  • 1
    What does the Django error page say? Commented Apr 19, 2016 at 7:26
  • @Primoz For example i leave field "first_name" empty and then i got error django page with: 'NoneType' object is not subscriptable except form page with error "This field is required". When i comment clean method then its work correct. Commented Apr 19, 2016 at 7:39
  • What is the 'vat' means in obj_user.username = form_client.cleaned_data['vat'] ? I think this is your mistake, can explain what is this? Commented Apr 19, 2016 at 8:09

1 Answer 1

14

You should do validation like that in the individual clean_<fieldname> methods. Those will only be called if the content is already valid and populated. So:

def clean_firstname(self):
    first_name = self.cleaned_data['first_name']
    return first_name[0].upper() + first_name[1:].lower()

def clean_last_name(self):
    last_name = self.cleaned_data['last_name']
    return last_name[0].upper() + last_name[1:].lower()

def clean_email(self):
    email = self.cleaned_data['email']
    return email.lower()

Note also your first_name and last_name validation could be simplified by using last_name.capitalize(), which converts to exactly the format you want.

Sign up to request clarification or add additional context in comments.

2 Comments

I have other problem when I leave field mailing_email blank and i'm getting: NOT NULL constraint failed: core_client.mailing_email - i did method clean_mailing_email which check field length and asign normal email when lenngth is 0. How resolve this? def clean_mailing_email(self): if len(self.cleaned_data['mailing_email'].lower()) > 0: return self.cleaned_data['mailing_email'].lower() else: return self.cleaned_data.get('email')
Well, that is the kind of thing that should be done in the main clean method, because again clean_mailing_email won't be called if the field is blank.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.