I have created a custom form and need to override both of the clean_field() method and clean() method. Here is my code:
class MyForm(forms.Form):
username=forms.RegexField(regex=r'^1[34578]\d{9}$')
code = forms.RegexField(regex=r'^\d{4}$')
def clean_username(self):
u = User.objects.filter(username=username)
if u:
raise forms.ValidationError('username already exist')
return username
def clean(self):
cleaned_data = super(MyForm, self).clean()
# How can I raise the field error here?
If I save this form twice, and the username will be already exist in the second time, the clean_username method will raise an error, however, the clean() method still run without interruption.
So my question is, how can I stop calling clean() when error already raise by cleaned_xxx, if that is not possible, then how can I raised the error again which raised by clean_xxxx() in clean() method?
In your clean method, you can check whether username is in the cleaned_data dictionary.
def clean(self):
cleaned_data = super(MyForm, self).clean()
if 'username' in cleaned_data:
# username was valid, safe to continue
...
else:
# raise an exception if you really want to
You probably don't need the else statement. The user will see the error from the clean_username method so you don't need to create another one.
Related
I am trying to create an update Serializer and return a message for Exception IntegrityError.
snippet of the particular lines of codes is below-
instance.username = validated_data.get('username', instance.username)
if User.objects.filter(username__iexact=instance.username).exists():
raise BaseException
How can i return a message which says username already exists without using Try and Except
update-
This is how i fixed this problem-
if User.objects.filter(username__iexact=instance.username).exists():
raise serializers.ValidationError("username already exists")
User.objects.filter(username__iexact=instance.username).exists()
This won't work because the instance is also User object so it will always return True, you need to exclude the current instance first.
Fixed query:
User.objects.exclude(pk=instance.pk).filter(username__iexact=instance.username).exists()
I don't know why you don't want to use try/catch but anyway, here's what you could do while following good patterns:
Django's User model already has the unique=True constraint.
Model instances that violate the UNIQUE constraint already throw an IntegrityError on call to save().
So, in your view you just need to catch for the IntegrityError and then raise serializers.ValidationError.
from rest_framework imports serializers, viewsets
from rest_framework.exceptions ValidationError
class UserProfileUpdateViewSet(viewsets.ModelViewSet):
def perform_update(self, serializer):
"""
Overwriting the method
"""
try:
serializer.save()
except IntegrityError:
raise ValidationError(
f"Username already taken. Please choose another one.",
)
As far as I know the clean() method cleans a form according to validation rules defined in forms.py.
For registering users I have created a form like this:
from django import forms
from django.contrib.auth.models import User
class RegistationForm(forms.Form):
# More fields ...
username = forms.CharField(
max_length=30, min_length=3, widget=forms.TextInput(
attrs={'placeholder': 'Username',
'title': 'You will use this for log in.'}))
# More fields ...
And for making sure that a new user does not use "#" char in username (and registers only one account with one email) I used this in my RegisterForm's clean method:
def clean(self):
cleaned_data = super(RegistationForm, self).clean()
user_email = cleaned_data.get("email")
user_username = cleaned_data.get("username")
if "#" in user_username: # Line causing error
msg = u"Usernames may contain alphanumeric, _, +, . and - characters."
self._errors["username"] = self.error_class([msg]
del cleaned_data["username"]
# More validation logic ...
return cleaned_data
But the problem is when I submit an empty form it gives me an argument of type 'NoneType' is not iterable error in error line shown in clean method.
Shouldn't Django first check if the form is OK and only then check the error line?
Or how do I fix it? What's my mistake?
As itsjeyd says, the error is because username is empty. However, a better solution is to define a clean_username method. This will only be called if the field is not empty, and is the preferred way of validating single fields - clean is best for validating fields that depend on each other. In clean_username you raise forms.ValidationError rather than inserting values on the errors dict.
If you leave the username field empty, cleaned_data.get('username') will return None. When checking whether "#" is in user_username, you are telling Python to iterate over the characters in user_username - which is None, so it can't be iterated over.
You could change your check to if user_username and "#" in user_username to guard against the NoneType is not iterable error.
I have read over the Forms and Formset Django documentation about 100x. To make this very clear, this is probably the first time I've ever used super() or tried to overload/inherit from another class (big deal for me.)
What's happening? I am making a django-model-formset in a view and I am passing it to a template. The model that the formset is inheriting from happens to be a ManyToMany relationship. I want these relationships to be unique, so that if my user is creating a form and they accidentally choose the same Object for the ManyToMany, I want it to fail validation.
I believe I have written this custom "BaseModelFormSet" properly (via the documentation) but I am getting a KeyError. It's telling me that it cannot find cleaned_data['tech'] and I am getting the KeyError on the word 'tech' on the line where I commented below.
The Model:
class Tech_Onsite(models.Model):
tech = models.ForeignKey(User)
ticket = models.ForeignKey(Ticket)
in_time = models.DateTimeField(blank=False)
out_time = models.DateTimeField(blank=False)
def total_time(self):
return self.out_time - self.in_time
The customized BaseModelFormSet:
from django.forms.models import BaseModelFormSet
from django.core.exceptions import ValidationError
class BaseTechOnsiteFormset(BaseModelFormSet):
def clean(self):
""" Checks to make sure there are unique techs present """
super(BaseTechOnsiteFormset, self).clean()
if any(self.errors):
# Don't bother validating enless the rest of the form is valid
return
techs_present = []
for form in self.forms:
tech = form.cleaned_data['tech'] ## KeyError: 'tech' <-
if tech in techs_present:
raise ValidationError("You cannot input multiple times for the same technician. Please make sure you did not select the same technician twice.")
techs_present.append(tech)
The View: (Summary)
## I am instantiating my view with POST data:
tech_onsite_form = tech_onsite_formset(request.POST, request.FILES)
## I am receiving an error when the script reaches:
if tech_onsite_form.is_valid():
## blah blah blah..
Isn't the clean method missing a return statement ? If I remember correctly it should always return the cleaned_data. Also the super call returns the cleaned_data so you should assign it there.
def clean(self):
cleaned_data = super(BaseTechOnsiteFormset, self).clean()
# use cleaned_data from here to validate your form
return cleaned_data
See: the django docs for more information
I used the Django shell to call the forms manually. I found that I was executing the clean() method on all of the forms returned from the view. There were 2 filled out with data, and 2 blank. When my clean() method was iterating through them all, it returned a KeyError when it got to the first blank one.
I fixed my issue by using a try-statement and passing on KeyErrors.
I have a django charField that is checked via the is_valid() method. The user is supposed to enter a valid logical expression in this field, so I wrote a parsing method that raises an exception if the expression is not correct.
How can I enhance the is_valid() method to cover this exception and display an error message to the user that his query was wrong?
I read this article (https://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute) but still have no idea how to do that.
try:
job = Job(user=request.user) # set the current user
form = JobForm(request.POST, instance=job)
if form.is_valid():
form.save()
job.execute()
messages.success(request, u'A new job with the query "{query}" was created.'.format(query=job.query))
return HttpResponseRedirect(reverse('job-index'))
return self.render_to_response({'job_form': form, 'is_new': True})
except ParseError:
return self.render_to_response({'job_form': form, 'is_new': True})
The try...except-Block should be done within the is_valid() method, that is my intention. Someone got any hints?
You've provided an answer to the question yourself - you create your own form (or model form) and perform custom validation on that form's field using its clean_'fieldname'() method. So for example, say your model is:
class Job(models.Model):
expression_field = models.CharField(...)
...
you create a forms.py:
class JobForm(forms.ModelForm):
pass
class Meta:
model = Job
def clean_expression_field(self):
# You perform your custom validation on this field in here,
# raising any problems
value = self.cleaned_data['expression_field']
if value is 'really_bad':
raise forms.ValidationError("bad bad bad")
return value
then make use of it in your views.py as you already are in your example. Now if the value the user enters doesn't meet your criteria, an exception will be automatically raised
I'm using Django forms. I'm validating in the model layer:
def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 5:
raise forms.ValidationError("Headline must be more than 5 characters.")
return title
However, there are some things that I need to validate in the views.py . For example...was the last time the user posted something more than a minute ago?
That kind of stuff requires request.user, which the models layer cannot get. So, I must validate in the views.py. How do I do something in the views.py to do the exact thing as this?
raise forms.ValidationError("Headline must be more than 5 characters.")
I think gruszczy's answer is a good one, but if you're after generic validation involving variables that you think are only available in the view, here's an alternative: pass in the vars as arguments to the form and deal with them in the form's main clean() method.
The difference/advantage here is that your view stays simpler and all things related to the form content being acceptable happen in the form.
eg:
# IN YOUR VIEW
# pass request.user as a keyword argument to the form
myform = MyForm(user=request.user)
# IN YOUR forms.py
# at the top:
from myapp.foo.bar import ok_to_post # some abstracted utility you write to rate-limit posting
# and in your particular Form definition
class MyForm(forms.Form)
... your fields here ...
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user') # cache the user object you pass in
super(MyForm, self).__init__(*args, **kwargs) # and carry on to init the form
def clean(self):
# test the rate limit by passing in the cached user object
if not ok_to_post(self.user): # use your throttling utility here
raise forms.ValidationError("You cannot post more than once every x minutes")
return self.cleaned_data # never forget this! ;o)
Note that raising a generic ValidationError in the clean() method will put the error into myform.non_field_errors so you'll have to make sure that your template contains {{form.non_field_errors}} if you're manually displaying your form
You don't use ValidationError in views, as those exceptions as for forms. Rather, you should redirect the user to some other url, that will explain to him, that he cannot post again that soon. This is the proper way to handle this stuff. ValidationError should be raised inside a Form instance, when input data doesn't validate. This is not the case.
You can use messages in views:
from django.contrib import messages
messages.error(request, "Error!")
Documentation: https://docs.djangoproject.com/es/1.9/ref/contrib/messages/