forms.ValidationError bug? - django

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

Related

Django - Boolean Fields on Checkbox Input

I have a ModelForm which has Boolean Field and CheckboxInput. The Boolean field provides values of 0 (False) & 1 (True) by default in the MYSQL Database.
The form is a modal and uses JSON to output the content from the Model. When the JSON file adds the content it enters either True or False.
The issue occurs when I try to change and submit the value of the Checkbox Input from False to True. When I try this I get the following message:
Oddly this works the other way around and allows me to submit the change from True to False.
Below is the code (I have only included the field with the issue.) The field is First_Time_Booking
Model.
first_time_booking = models.BooleanField()
Form Widget.
'first_time_booking': CheckboxInput(attrs={'class': 'form-check-input', 'id': 'bfbw_edit_first_time_booking', 'name': 'bfbw_edit_first_time_booking'}),
View
def update_bfbw(request):
if request.method == 'POST':
bfbw_booking_id = request.POST.get('bfbw_booking_ref')
bfbw_data = BFBWBookings.objects.get(id=bfbw_booking_id)
bfbw_data.school_name = request.POST['school_name']
bfbw_data.first_time_booking = request.POST.get('first_time_booking', False)
bfbw_data.save()
else:
print(form.errors)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
I have tried writing an If function to alter the value, this then works to change the value to On but not when switching it off. I then get the below message.
if request.POST['first_time_booking'] == 'on':
bfbw_data.first_time_booking = True
else:
bfbw_data.first_time_booking = False
Any help on this will be great. Thanks in advance for any replies.
The value is either "on" (if the user has checked the checkbox), or does not exists (if the user has not checked the checkbox).
You thus can work with:
bfbw_data.first_time_booking = request.POST.get('first_time_booking') == 'on'
Note: It is better to use a Form [Django-doc]
than to perform manual validation and cleaning of the data. A Form will not
only simplify rendering a form in HTML, but it also makes it more convenient
to validate the input, and clean the data to a more convenient type.

How to extract Django Form errors message without the HTML tags

I need to extract the messages and field.
For Example, I have this django form error result
<ul class="errorlist">
<li>__all__
<ul class="errorlist nonfield">
<li>Pointofsale with this Official receipt and Company already exists.</li>
</ul>
</li>
</ul>
from the output of this code
def post_sale(request):
sale_form = request["data"]
if sale_form.is_valid():
save_form.save()
else:
print save_form.errors
But what i need to achieve is to get the message without the tags, so i could just return those message in plain string/text.
def post_sale(request):
sale_form = request["data"]
if sale_form.is_valid():
save_form.save()
else:
# This is just pseudo code
for field in save_form.errors:
field = str(field["field"})
message = str(field["error_message"])
print "Sale Error Detail"
print field
print message
error_message = { 'field':field,'message':message }
error_messages.append(error_message )
The output would be:
Sale Error Detail
(the field where form error exists)
Pointofsale with this Official receipt and Company already exists.
Explored Questions and Documentations
displaying django form error messages instead of just the field name
Getting a list of errors in a Django form
django form errors. get the error without any html tags
How do I display the Django '__all__' form errors in the template?
https://docs.djangoproject.com/en/1.10/ref/forms/api/
https://docs.djangoproject.com/en/1.10/topics/forms/
Thanks, please tell if something is amiss or something needs clarification so i could fix it.
The errors property of a bound form will contain all errors raised by that form, as a dictionary. The key is a field or other special values (such as __all__), and the value is a list of one or more errors.
Here is a simple example on how this works:
>>> from django import forms
>>> class MyForm(forms.Form):
... name = forms.CharField()
... email = forms.EmailField()
...
>>> f = MyForm() # Note, this is an unbound form
>>> f.is_valid()
False
>>> f.errors # No errors
{}
>>> f = MyForm({}) # Now, the form is bound (to an empty dictionary)
>>> f.is_valid()
False
>>> f.errors # dictionary of errors
{'name': [u'This field is required.'], 'email': [u'This field is required.']}
In your view, depending on what you want you can just return the value of form.errors, or parse it to whatever structure your need.
for field, errors in form.errors.items():
print('Field: {} Errors: {}'.format(field, ','.join(errors))
For the specific error you have mentioned, it is a custom error raised as a result of overriding the clean() method - which is why it is listed under the special identifier __all__ and not under a specific field.
This is mentioned in the forms reference, under validation:
Note that any errors raised by your Form.clean() override will not be
associated with any field in particular. They go into a special
“field” (called __all__), which you can access via the
non_field_errors() method if you need to. If you want to attach errors
to a specific field in the form, you need to call add_error().

Can you use a Django form multiple times in one view?

I have a Django view that uses one form multiple times. The form is just a boolean field form that is supposed to initialize to True but then the user can decide to uncheck the boxes or not.
The problem I'm having is that the all of the fields evaluate to True no matter what the user leaves checked. Is this a problem with using the same form multiple times, or did I mess something else up?
The form looks like this:
class DataTypeForm(forms.Form):
def __init__(self,*args,**kwargs):
section_label = kwargs.pop('section_label')
initial_value = kwargs.pop('initial_value')
super(DataTypeForm,self).__init__(*args,**kwargs)
self.fields['dataType'].label=mark_safe(section_label)
self.fields['dataType'].initial=initial_value
self.fields['dataType'].required=False
dataType = forms.BooleanField(required=False)
This is the view:
def Manual_Request(request):
form_dict = {}
arg_dict = {}
message = message = {'last_url':'Nominal_Request'}
if request.method == 'POST':
logger.info("Query submitted, beginning query results render for:")
form_NOM = DataTypeForm(request.POST or None,section_label="ENG_NOM",initial_value=True)
form_DDM = DataTypeForm(request.POST or None,section_label="SCI_DDM",initial_value=True)
form_RAW = DataTypeForm(request.POST or None,section_label="SCI_RAW",initial_value=False)
if form_NOM.is_valid():
NOM = form_NOM.cleaned_data['dataType']
arg_dict.update({'ENG_NOM':str(NOM)})
logger.info("ENG_NOM: {val}".format(val=NOM))
if form_DDM.is_valid():
DDM = form_DDM.cleaned_data['dataType']
arg_dict.update({'SCI_DDM':str(DDM)})
logger.info("SCI_DDM: {val}".format(val=DDM))
if form_RAW.is_valid():
RAW = form_RAW.cleaned_data['dataType']
arg_dict.update({'SCI_RAW':str(RAW)})
logger.info("SCI_RAW: {val}".format(val=RAW))
return Request_Results(request,args_dict)
else:
logger.info("Rendering query page")
form_NOM = DataTypeForm(section_label="ENG_NOM",initial_value=True)
form_DDM = DataTypeForm(section_label="SCI_DDM",initial_value=True)
form_RAW = DataTypeForm(section_label="SCI_RAW",initial_value=True)
form_dict.update({'form_NOM':...etc})
return render(request,'InterfaceApp/COMMRequest_Manual.html',form_dict)
Help much appreciated!
I haven't run your code, but my best guess is that yes, it's a problem with using the same form multiple times in the same view. The reason? All of your <input type="checkbox" name="..." ... /> tags will have the same name, 'dataType'. The user's browser knows nothing of your back-end, and will just send, for example, dataType=on&dataType=on as POST data for the three fields if two are checked and one is not.
Seeing the problem here? How is django supposed to know which of those dataType fields are for your NOM, DDM, or RAW forms? It can't know.
You should be able to solve this using form prefixes. In short, there's a kwarg that you can pass to a form's __init__() that will cause a prefix to be added to all of the form items in the rendered HTML. So, for example:
form_NOM = DataTypeForm(request.POST or None, section_label="ENG_NOM",
initial_value=True, prefix="NOM")
form_DDM = DataTypeForm(request.POST or None, section_label="SCI_DDM",
initial_value=True, prefix="DDM")
form_RAW = DataTypeForm(request.POST or None, section_label="SCI_RAW",
initial_value=False, prefix="RAW")
Hope that helps!
This is exactly what Django formsets are for. They allows you to create a set of the same type of form. It handles prefixes, and adds a management form so that Django doesn't get confused as to what data comes from what form.
https://docs.djangoproject.com/en/1.8/topics/forms/formsets/

submitting form results into db - django

i created a form to save a post into db for my blog project. I've designed index page. now i am tryin to create a form to create new posts. before that i was using ' manage.py shell'
here is my view :
def addpost(request):
form = addForm()
if request.method=="POST":
titleform = request.POST['title']
bodyform = request.POST['body']
checkform = request.POST['isdraft']
if form.is_valid():
n = Post(title = titleform, body = bodyform, isdraft=checkform)
n.save()
return HttpResponseRedirect('/admin/')
else:
pass
return render(request,'userside/add.html',{'form':form,})
my model.py:
class Post(models.Model):
title = models.CharField(max_length = 100)
body = models.TextField()
slug = AutoSlugField(populate_from='title',unique=True)
posted = models.DateField(auto_now_add=True)
isdraft = models.BooleanField()
def __unicode__(self):
return self.title
#permalink
def get_absolute_url(self):
return ('view_blog_post',None, {'postslug':self.slug})
class addForm(forms.Form):
title = forms.CharField(max_length=100)
body = forms.CharField(widget=forms.Textarea)
isdraft = forms.BooleanField()
if i submit form as 'isdraft' field is False(unchecked) ; it gives error like:
MultiValueDictKeyError at /admin/addpost/
"Key 'isdraft' not found in "
and if i submit the form as 'isdraft' field is True(checked) ; it gives nothing. just refreshing form. no adding data into db.
i am doing sth wrong..
thank you
edit : Dmitry Beransky's answer worked for checkbox error. but it still doesnt add any data into db. just refreshes the form.
The whole point of using a form is that it takes care of validation and cleaning, that is converting values to the proper data types. That's why you should be accessing form.cleaned_data rather than reques.POST, and you should be doing it inside the if form.is_valid() check.
Edit
I've just noticed that you're never passing request.POST to the form. So form.is_valid() will never be true.
Please go back and read the documentation about using a form in a view.
If a checkbox is not checked in your HTML form, it's name/value is not going to be included in the data that the browser sends to your server. Which meanst that the request.POST dictionary is not going to contain an entry for 'isdraft' which in turn will cause a key error when you try to read the isdraft value. A solution is to change the way you read the value from the posted data to:
checkform = request.POST.get('isdraft', False)
rather than throw an error if isdraft isn't found in the dictionary, this will set checkform to False (the default value in case of a missing key)
Maybe your form does not validate at all. Have you checked if your code even reaches those lines after the if form.is_valid() statement ? If they do, what you've done there is right and should create the db row for your new entry, though you could have used
Post.objects.create(....) , and that would have taken away the need for calling the method save().
Some points though:
instead of checking for request.POST , check for request.method == 'POST' , cause there might be a post which has an empty POST dict ( in case no arguments have been submitted ), in that case request.POST fails to provide the right check .
see the docs for more info : request.POST
instead of using request.POST['var_name'] , use request.POST.get('var_name', 'default_value') , cause doing this like request.POST['var_name'] might result in some exceptions ( in case for example the argument is not provided , like what happened for your checkform variable )
Try accessing those variables through form.cleaned_data
and finally , you don't need the else statement in the end , just use the indentation :)

Help understanding a Django view

I am trying to follow the code listed on https://github.com/alex/django-ajax-validation/blob/master/ajax_validation/views.py
I have been able to understand a small chunk of it. I have added comments stating my understanding of what is happening.
I would really appreciate some assistance on questions I listed in comments next to the lines I couldn't quite follow.
def validate(request, *args, **kwargs):
# I thing it is some sort of initializations but I cannot really understand what's happening
form_class = kwargs.pop('form_class')
defaults = {
'data': request.POST
}
extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
kwargs = extra_args_func(request, *args, **kwargs)
defaults.update(kwargs)
form = form_class(**defaults)
if form.is_valid(): #straightforward, if there is no error then the form is valid
data = {
'valid': True,
}
else:
# if we're dealing with a FormSet then walk over .forms to populate errors and formfields
if isinstance(form, BaseFormSet): #I cannot really understand what is BaseFromSet
errors = {}
formfields = {}
for f in form.forms: # I am guessing that this is for when there are multiple form submitted for validation
for field in f.fields.keys(): # I think he is looping over all fields and checking for error. what does add_prefix () return? and what is formfields[]?
formfields[f.add_prefix(field)] = f[field]
for field, error in f.errors.iteritems():
errors[f.add_prefix(field)] = error
if form.non_form_errors():
errors['__all__'] = form.non_form_errors() # what is the '__all__'?
else:
errors = form.errors
formfields = dict([(fieldname, form[fieldname]) for fieldname in form.fields.keys()])
# if fields have been specified then restrict the error list
if request.POST.getlist('fields'): # I am having a hard time understanding what this if statement does.
fields = request.POST.getlist('fields') + ['__all__']
errors = dict([(key, val) for key, val in errors.iteritems() if key in fields])
final_errors = {} # here the author of this code totally lost me.
for key, val in errors.iteritems():
if '__all__' in key:
final_errors[key] = val
elif not isinstance(formfields[key].field, forms.FileField):
html_id = formfields[key].field.widget.attrs.get('id') or formfields[key].auto_id
html_id = formfields[key].field.widget.id_for_label(html_id)
final_errors[html_id] = val
data = {
'valid': False or not final_errors,
'errors': final_errors,
}
json_serializer = LazyEncoder() # Why does the result have to be returned in json?
return HttpResponse(json_serializer.encode(data), mimetype='application/json')
validate = require_POST(validate) # a decorator that requires a post to submit
LazyEncoder
class LazyEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, Promise):
return force_unicode(obj)
return obj
form_class = kwargs.pop('form_class')
This is simply pulling the keyword argument, form_class, that was passed in via the URL conf.
(r'^SOME/URL/$', 'ajax_validation.views.validate',
{'form_class': ContactForm}, # this keyword argument.
'contact_form_validate')
BaseFormSet is simply the formset class doing the work behind the scenes. When you don't know, search the source! grep -ri "baseformset" . It's an invaluable tool.
Take a look at at django.forms.formsets to see how formset_factory produces new "formset" classes based on the BaseFormSet, hence the factory part!
I am guessing that this is for when there are multiple form submitted for validation
Yes, that's exactly what a formset is for (dealing with multiple forms)
I think he is looping over all fields and checking for error. what does add_prefix () return? and what is formfields[]?
Yes, that would be looping through the field names.
add_prefix() is for prefixing form field names with a specific form. Because a formset repeats form elements multiple times, each field needs a unique prefix, such as 0-field1, 1-field1, etc.
formfields is just an empty dictionary defined a few lines above.
what is the 'all'?
__all__ is defined at the top of django.forms.forms
NON_FIELD_ERRORS = '__all__'
It's just what non field specific errors (such as constraints across 2 fields) are stored under in the errors dictionary as opposed to errors[fieldname].
I am having a hard time understanding what this if statement does.
The author has left a note:
# if fields have been specified then restrict the error list
if request.POST.getlist('fields'):
It's checking if you specified any specific fields to validate in your URLConf, this is not django but ajax_validation.
You can see that he's overwriting his errors dictionary based on only the fields specified, thus passing on the validation only for those fields.
errors = dict([(key, val) for key, val in errors.iteritems() if key in fields])
here the author of this code totally lost me.
The author has mapped a custom errors and fields dictionary to specific field names with prefixes, (as opposed to the usual FormSet with each form having its own errors dictionary, unaware of the formset itself) which he presumably uses in the AJAX response to validate all fields.
Normally, you can iterate over a formset and go through the errors on a form by form basis, but not so if you need to validate all of them through ajax.
The line pulling html_id should be straight forward most of the time, but it's there because form widgets CAN add interesting things to the end of the ID's based on whether or not the widget is a radio select for example.
From source comments :
# RadioSelect is represented by multiple <input type="radio"> fields,
# each of which has a distinct ID. The IDs are made distinct by a "_X"
# suffix, where X is the zero-based index of the radio field. Thus,
# the label for a RadioSelect should reference the first one ('_0').
Why does the result have to be returned in json?
Because it's an ajax request and javascript easily eats json.
2- could you go through these lines of code...
extra_args_func = kwargs.pop('callback', lambda request, *args, **kwargs: {})
Either return a keyword argument named 'callback' (which if passed in, is supposed to be a function that accepts request and return a dictionary), and if it wasn't, return a lambda function that only returns an empty dictionary.
I'm not sure what the specific use is for the extra context. You could use it to run arbitrary snippets of code without modifying or subclassing ajax_validation...
It might help you to run this code, and put a debugger breakpoint in somewhere so you can step through and examine the variables and methods. You can do this by simply putting this line where you want to break:
import pdb; pdb.set_trace()
and you will be dumped into the debugger in the console.