Check form errors in Django testing - django

I want to test field errors using assertFormError. How can I test it?
In forms.py
password = forms.RegexField(max_length=254,
error_messages={'required': _('This is required.')
'invalid': _('It is invalid)}
)
In tests.py
form = UserForm(data=data)
self.assertContains(form['password'].errors, 'It is invalid')

Here is one way you can test it. Note that you need to call the is_valid function:
self.assertFalse(form.is_valid())
self.assertEqual(form.errors['password'][0], 'It is invalid')

first you need to call is_valid method and then chceck if errors list has specific error like:
self.assertFalse(form.is_valid())
self.assertIn('password', form.errors.keys())
self.assertIn('It is invalid', form.errors['password'])

Related

forms.ValidationError bug?

this is my third day in django and I'm working on my validation form and i came across this weird bug where my validation form didn't do anything so here's the code
class RegisterForm(forms.ModelForm):
email = forms.EmailField(label="E-Mail", error_messages={'required': 'Please enter your name'})
class Meta:
model = LowUser
fields =['fullname', 'email', 'password', 'bio']
widgets = {
'password' : forms.PasswordInput()
}
def clean_fullname(self):
data = self.cleaned_data.get("fullname")
if 'ab' in data:
raise forms.ValidationError('invalid')
else:
return data
if i input "ac" to the fullname it works perfectly fine it adds the input to the database. But if i input "ab" it didn't do anything it doesn't give me any errors nor add the input to my database. And I'm pretty sure my forms.ValidationError is bugging because if i change my raise forms.ValidationError('invalid') to raise NameError('Test') like this
def clean_fullname(self):
data = self.cleaned_data.get("fullname")
if 'ab' in data:
raise NameError('Test')
else:
return data
and i input "ab". It works completely fine and it gave me this page
and I'm using django 2.1.5 if you're wondering i would appreciate any help
thank you in advance
If i input "ac" to the fullname it works perfectly fine it adds the input to the database. But if i input "ab" it didn't do anything it doesn't give me any errors nor add the input to my database.
That is expected behavior, ValidationErrors are used to collect all errors.
The idea is that you raise ValidationErrors. These are all collected, and when one such error is present, form.is_valid() will return False, and the form.errors will contain a dictionary-like object with all the errors. The reason this is done is to collect problems with all fields in one single pass, such that it does not only report the first error of the form data.
Imagine that you have five fields with mistakes, and the form only reports problems with the first field. Then it takes five rounds before all fields are validated. By collecting all errors, it can show multiple ones. You can even return multiple errors on the same field.
For more information, see the raising ValidationError` section of the Django documentation.
Thanks to Willem i realized that the problem was in my views.py.
def registerForm(request):
regisform = RegisterForm()
cntxt = {'mainregisform': regisform, 'tst': ''}
if request.method == 'POST':
regisform = RegisterForm(request.POST)
if regisform.is_valid():
regisform.save()
return render(request, 'userregister.html', cntxt)
i thought that the ValidationError wasn't giving me any errors because usually there's an error message on top of my input box, but it actually did gave me an error. the problem was i define the mainregisform before the regisform got re-rendered therefore i never got error message

Validation not running in django form

I want to run field validatin on my form, as in form and field validation- using validation in practice.
My form looks like this:
from kapsule.validators import name_zero_min_length, name_max_length
class NameUpdateForm(forms.Form):
name = forms.CharField(
validators=[
name_zero_min_length,
name_max_length
]
)
My validators:
from django.core.exceptions import ValidationError
def name_zero_min_length(name_field):
# Check minimum length
if not len(name_field) > 0:
print('firing zero length')
raise ValidationError(
"My custom error message name must be at least one character"
)
def name_max_length(name_field):
# Check that the name is under the max length
MAX_LENGTH = 200
if len(name_field) > MAX_LENGTH:
print('raising')
raise ValidationError(
"My custom error message name cannot be more than {} characters".format(MAX_LENGTH)
)
My view like this:
def edit_kapsule_name(request, kapsule_pk):
kapsule = Kapsule.objects.get(pk=kapsule_pk)
form = NameUpdateForm(request.POST)
response = {}
print('pre-validation')
if form.is_valid():
print('VALID')
name = form.data.get('name')
kapsule.name = name
kapsule.save(update_fields=['name'])
else:
print('INVALID') # INVALID
print('json') # json
errors = form._errors.as_json()
print(errors) # {"name": [{"message": "This field is required.", "code": "required"}]}
My output is commented in the above code (invalid, and giving a different error that that which I expected).
Why is my custom validation not running?
This seems to match with my model validation (working), and the second reponse here
Well in the comment from your code I can see that the form is invalid and it is complaining about a required field. That might be the cause your validators are not running, according to the docs:
The clean() method on a Field subclass is responsible for running to_python(), validate(), and run_validators() in the correct order and propagating their errors. If, at any time, any of the methods raise ValidationError, the validation stops and that error is raised. This method returns the clean data, which is then inserted into the cleaned_data dictionary of the form.
On the other hand, if the field is required, the validation not len(name_field) > 0 has no much sense.
Try calling your validators as part of the clean_name method in your form.

Django 2 Test response for valid and invalid form

I made a TestCase to check if I would get the proper response and page redirection, but It's not working as I thought it would. When I tried a valid form I got the response I expected, but when I made it invalid, I still got the same response.
views.py (I left off the 'GET' 'else:')
def create_employee_profile(request):
if request.POST:
name_form = EmployeeNameForm(request.POST)
if name_form.is_valid():
new_name_form = name_form.save()
return redirect(new_name_form)
else:
return render(request,
'service/create_or_update_profile.html',
{'name_form': name_form}
)
Test.py
class TestCreateEmployeeProfileView(TestCase):
def test_redirect_on_success(self):
response = self.client.post('/service/', {
'first_name': 'Test', # Required
'middile_name': 'Testy', # Optional
'last_name': '', # Required
})
self.assertEqual(response.status_code, 200)
I guess while I am question, I might as well ask how to access the redirect to test that as well.
On success, the new path should be /service/name/1/, the '1' being the 'pk' of the created object.
I know I've seen SimpleTestCase, but I haven't found a good example or tutorial on how to use it.
If you always get a 200, that is because your form is always invalid. Your view redirects on successful save, which is a 302.
The way to test that the form has saved is to check that the new item is indeed in the database:
self.assertTrue(Employee.objects.filter(first_name='Testy').exists())
or whatever.
Here are two scenarios:
Your form is valid, it will be saved and will be redirected to new_name_form that means a successful redirection. Since it is a successful redirection, you will get status code 200.
The same thing will happen when your form is invalid, i.e it will start rendering the create_or_update_profile page. Hence successful rendering and 200 status code.
So in either way, you will get successful redirection.
If you want to check the form, this is the better approach to do:
from form import EmployeeNameForm
class TestCreateEmployeeProfileView(TestCase):
def test_redirect_on_success(self):
form = UserForm(data='first_name': 'Test', # Required
'middile_name': 'Testy', # Optional
'last_name': '',)
self.assertTrue(form.is_valid())
def test_redirect_on_failure(self):
form = UserForm(data='first_name': 'Test', # Required
'middile_name': 'Testy', # Optional
'last_name': '',)
self.assertFalse(form.is_valid())
There will be no need to test the redirection. It surely will work fine, if the form is valid.
Hope that helped.

django form: pass parameter to is_valid

I have an abstract form and 2 forms using it. In the is_valid method of the abstract form, I check for a condition and that condition depends on the form calling the method.
The line is:
if eval(self.cleaned_data.get("validated_modif")):
According to the form, it should be replaced by one of the following lines:
if act.validated_attendance==0: #MinAttend form
if act.validated<2: #Act form
I have a code that works. But it is very (very) dirty, I am looking for a better idea.
forms.py:
class AbstractModif(forms.Form):
releve_mois_modif=forms.IntegerField(min_value=1, max_value=12)
class Meta:
#abstract form
abstract = True
#check if the searched act already exists in the db and has been validated
def is_valid(self, *args, **kwargs):
# run the parent validation first
valid=super(AbstractModif, self).is_valid()
# we're done now if not valid
if not valid:
return valid
#if the form is valid
Model=Act
fields={}
fields["releve_mois"]=self.cleaned_data.get("releve_mois_modif")
try:
act=Model.objects.get(**fields)
if Model!=Act:
act=act.act
#MinAttend form: check act.validated_attendance=0
#Act form: check act.validated<2
if eval(self.cleaned_data.get("validated_modif")):
self._errors['__all__']=ErrorList([u"The act you are looking for has not been validated yet!"])
return False
except Exception, e:
self._errors['__all__']=ErrorList([u"The act you are looking for doesn't exist in our database!"])
print "exception", e
return False
# form valid -> return True
return True
form_1.py:
class Modif(AbstractModif):
#fake field for the is_valid method
validated_modif=forms.CharField(widget=forms.HiddenInput(), initial="act.validated<2")
form_2.py
class Modif(AbstractModif):
#fake field for the is_valid method
validated_modif=forms.CharField(widget=forms.HiddenInput(), initial="act.validated_attendance==0")
form.html
<!-- hidden field for the is_valid method of the form -->
{{ modif.validated_modif }}
I use eval and the initial value of a hidden field to check the condition. Do you have a nicer solution?
Thank you!
This is an immensely bad idea. You're running eval on input received from the browser. So if I use the browser dev tools to modify the contents of the hidden field to os.system('rm -rf /'), what do you think would happen?
I can't see any need for this at all. You have two form subclasses; why don't you simply put the validation in a method in those subclasses?
class Form1(AbstractForm):
def validate_modif(self, act):
return act.validated < 2
class Form(AbstractForm):
def validate_modif(self, act):
return act.validated_attendance == 0
and you can simply call self.validate_modif(act) to perform the validation.
Note also that you should not be overriding is_valid(), but clean(). And your Meta class does nothing because this is a normal Form, not a ModelForm.

How can I test an unchecked checkbox in Django?

So I present a checkbox to the user, as you can see inside my forms.py file:
class AddFooToBarForm(forms.Form):
to_original = forms.BooleanField(initial=True,
error_messages={'required':
"Oh no! How do I fix?"})
...
Depending on whether the user checks or unchecks that checkbox, I do something different, as you can see inside my views.py file:
def add_foo_to_bar(request, id):
...
try:
bar = Bar.objects.get(pk=id)
if request.method == 'POST': # handle submitted data
form = AddFooToBarForm(bar, request.POST)
if form.is_valid(): # good, now create Foo and add to bar
...
to_original = form.cleaned_data['to_original']
print 'to original is {0}'.format(to_original) # for debugging
if to_original:
# do A
else:
# do B
...
So I want to test that my site does indeed perform the correct actions, depending on whether the user checks the checkbox or not, inside my tests.py file:
class FooTest(TestCase):
...
def test_submit_add_to_Bar(self):
form_data = {
...
'to_original': True,
}
response = self.client.post(reverse('add_foo_to_bar', args=(self.bar.id,)),
form_data, follow=True)
self.assertContains(...) # works
...
form_data['to_original'] = None
response = self.client.post(reverse('add_foo_to_bar', args=(self.bar.id,)),
form_data, follow=True)
print response # for debugging purposes
self.assertContains(...) # doesn't work
I've tried
del form_data['to_original'] -- this gives me the "Oh no! How do I fix?" error message
form_data['to_original'] = None -- in my view function, I get True, so A is done instead of B
form_data['to_original'] = False -- this gives me the "Oh no! How do I fix?" error message once again
So how should I test the user not checking the checkbox in Django? (I'm using the latest version, 1.4.3)
When checkbox is not checked, its not present in submitted form. Also, when submitted value is 'on'.
If you want to make BooleanField optional have required=False while defining the form field.
Documentation BooleanField.