Django: Displaying formset errors correctly - django

I have an inline formset for a model, which has a unique_together constraint. And so, when I input data, which doesn't fulfill this constraint, it displays:
__all__Please correct the duplicate values below.
The code, which does this is:
{% for error in formset.errors %}
{{ error }}<br/>
{% endfor %}
I don't much like the __all__ at the beginning of the error and it is quite clearly the dictionary key, so I tried:
{% for key, error in formset.errors %}
{{ key }}: {{ error }}<br/>
{% endfor %}
But then all I get is:
__all__:
{{ error }} won't display at all. So what's going on here? And how do I display the error correctly?

I think the problem here is that formset.errors is a list of dictionaries, not a single dictionary.
From the Django docs page on formsets:
>>> formset.errors
[{}, {'pub_date': [u'This field is required.']}]
See if something like this fixes the problem: (Updated based on the askers comments)
{% for dict in formset.errors %}
{% for error in dict.values %}
{{ error }}
{% endfor %}
{% endfor %}
If that fails, I'd try using manage.py shell, and try to reproduce your situation in the python shell... that way it will be easy to inspect the various values and figure out what you need to do.

The for loops are unnecessary, these errors should be correctly displayed with the following:
{{ formset.non_form_errors }}

Here is a clarification for anyone encountering similar issues of errors not being rendered in template:
If you have and error regarding the formset as a whole, use:
{{ formset.non_form_errors }}
this basically returns errors in __all__ entry from formset.errors. It is documented as:
"""
Returns an ErrorList of errors that aren't associated with a particular
form -- i.e., from formset.clean(). Returns an empty ErrorList if there
are none.
"""
However if you are rendering forms from formset and some errors are not being renderd, you are probably missing:
{% for form in formset.forms %}
{# ... #}
{{ form.non_field_errors }}
{% endfor %}
this returns errors in __all__ entry from form.errors. Those are, analogous to the non_form_errors, the errors that aren't associated with a particular field, but rather with the field relations. For example if you had a form with fields From and To, and you validate if From value is smaller then To value, the tag {{ form.non_field_errors }} could render the following error:
'The From value must be smaller than the To value'

Django 1.6 formsets have a new method, BaseFormSet.total_error_count. Using this in a template conditional ensures you only will output errors and markup if at least one error exists in the formset.
{% if formset.total_error_count %}
<ul class="errorList">
{% for dict in formset.errors %}
{% for error in dict.values %}
<li>{{ error }}</li>
{% endfor %}
{% endfor %}
</ul>
{% endif %}
See the Django docs page for v1.6+.

Related

Get formset name variables in django templates

I am sending a formset to my template. I want to add some style to it, so I need to access his variables.
I took the following code from documentation, where they print the form and it has some attributes, amongside them the name="form-0-title".
for form in formset:
print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title"></td></tr>
...
As I want to style my forms, I need to give the inputs a name to know how to treat them in the backend. When I do the following in the templates
{{ proficiency_form.management_form }}
{% for form in proficiency_form %}
{{form.title.name}}
{% endfor %}
I get title, title, ... instead of id_form-0-title, id_form-1-title, ...
How can I get the correct name in the templates?
You can easily get the input's name of your fields like so:
{{ proficiency_form.management_form }}
{% for form in proficiency_form %}
{{form.title.html_name}}
{% endfor %}
If you want to get the label's id, you can consider this approach:
{{ proficiency_form.management_form }}
{% for form in proficiency_form %}
{{form.title.id_for_label}}
{% endfor %}
Read more about it in the official Docs.
You can find a similar(almost duplicate) question here.

Django check form error code in template

I am using Django 1.10 and trying to find a way to check the error code of a form inside of the template. So I tried a few things, like errors.as_data or errors.as_json, but I was unable to parse the different values (except by using javascript). Could it be something like this ?
<p>{% for key, value in form.errors.items %}
{{ value }}
{% if code == 'inactive_account'%}
// do some stuff
{% endif %}
{% endfor %}
</p>
But I don't know how to get this error code. Any suggestion ?
PS : I know that a solution would be to do it inside of the view, but since I am using a django already-made one, I would prefer not to do it.
The dictionary form.errors does not contain the ValidationError instances. You need to use the as_data method.
Note that you need to loop through the list of errors for each key, and then you can check the code.
{% for key, key_errors in form.errors.as_data.items %}
{{ key }}
{% for error in key_errors %}
{% if error.code == 'inactive_account'%}
// do some stuff
{% endif %}
{% endfor %}
{% endfor %}
There's also other way to check for the error code.
You can use has_error to check for the error code.
{% form.has_error 'field_name' 'code' %}
To check for non-field errors use NON_FIELD_ERRORS as the field parameter.

Better way to re-order Django form errors?

Is there a better, more "Django-istic", way to put Django form error messages in a particular order than the technique shown below? I have a form with ten fields in it. If the user doesn't fill out a required field or if they enter invalid data, I iterate through the form's error fields which I've put in a custom error list by displaying a red "x" and the error message(s) at the top and a red "x" next to the invalid field(s) below:
{# template.html #}
<form method="post" action=".">
{% csrf_token %}
{# Non-field errors removed for clarity #}
{# Iterate through error messages in custom ordered error list #}
{% for error_message in ordered_error_list %}
<img src='/static/img/red_x.png' alt='Error'></img>
{{ error_message }}<br>
{% endfor %}
<br>
{% for field in form.visible_fields %}
<div>
{{ field.label_tag }}<br />
{{ field }}
{% if field.errors %}
<img src='/static/img/red_x.png' alt='Error'></img>
{% endif %}
</div>
{% endfor %}
{# Hidden fields removed for clarity #}
<p><input type="submit" value="Continue" /></p>
</form>
Here's my view and helper function:
# views.py
def create_member_profile(request):
if request.method == "POST":
form = CreateMemberProfileForm(request.POST)
if form.is_valid():
# Process form data and redirect to next page...
return HttpResponseRedirect(reverse('review-member-profile'))
# Before I re-POST the form and display the errors, I'll put the errors in order
else:
ordered_error_list = put_member_profile_errors_in_order(form)
else:
form = CreateMemberProfileForm()
return render_to_response(template, locals(), context_instance=RequestContext(request))
def put_member_profile_errors_in_order(form):
errors_in_order = [] # List
for error in form['field_1'].errors:
errors_in_order.append(error)
for error in form['field_2'].errors:
errors_in_order.append(error)
for error in form['field_3'].errors:
errors_in_order.append(error)
# ...
for error in form['field_10'].errors:
errors_in_order.append(error)
return errors_in_order
The reason this all seems necessary is that form.errors is a dictionary and Python dictionaries, by definition, are unordered. However, as I said, I want any error messages at the top to be displayed in the same order as the form fields they refer to. I couldn't find any form.errors attributes that would allow me to re-order the form errors. I don't like the "for error in form[]" blocks but they seem to be required if I want to strip the HTML tag from the beginning of each error. Also note that I'm not checking for errors by putting each "for" loop inside an "if form['field'].errors" block because omitting this doesn't make a difference and I want this code to run as fast as possible.
Is there a better way to do this? Thanks!
You could do something like this. Same idea s you are using (two for loops).
<!-- near the top -->
<tr>
{% for field in form %}
<td>{{ field.label_tag }}: {{ field.errors }}</td>
{% endfor %}
</tr>
<!-- your form -->
{% for field in form %}
<tr>
<td>{{ field.label_tag }}:</td>
<td>
{{ field }}
{{ field.help_text }}
</td>
</tr>
{% endfor %}
Format this as you wish, but I think that kind of gives you an idea. You can also customize the error messages in your form clean:
from django import forms
YourForm(forms.Form):
....
def field_clean(self): # or use clean(self) for multiple fields
....
raise forms.ValidationError(u'Your custom error')
....
More on form field validation from Django docs.

Control the form errors display while using {{ form.as_ul }} in Django templates.

I like the convenient output form method {{ form.as_ul }} but is there a way I can still continue to use it but capture all the errors upfront instead of displaying the error just above each field.
I understand that there are ways to loop through each form element and so on as mentioned in django docs but I want to continue to utilize the capability of form.as_ul() except get control over error display.
Solved this problem by using Reusable Form Templates.
Its simple...
Create a reusable template with the following code snippets based on your need.
If you want to display all errors right at the top of the form...
{% if form %}
{% if form.errors %}
{% for field in form %}
{{field.errors}}
{% endfor %}
{% endif %}
{% for field in form %}
<li>{{ field.label_tag }}: {{ field }}</li>
{% endfor %}
{% endif %}
If you want to display all errors right after each form field without the default html elements around error use...
{% for field in form %}
{{ field.label_tag }}: {{ field }}
{% for error in field.errors %}{{ error }}{% endfor %}
{% endfor %}
Used the second template and created a Inclusion Tag
The only way I see is to inherit the new form class from forms.Form and alter as_ul method as you like. Which isn't very good if you are going to use third-party forms like login form and so on (they won't have this method).
I think the best solution is to create your own inclusion tag and render form with it. It will be as short as as_ul ({% render_form form %}) but very flexible, it will work with all forms and won't mix HTML and Python code.
I still think the customization for rendering form in templates is quite flexible. I usually do this for my webapps. You may work with a bit javascript and css but not a big problem. Moreover, I think we should try to make the app simple.

Django Newbie ManyToManyField Template Question

I have a Django model with a ManyToManyField and I'm trying to iterate the contents of that field in a comma-delimited list in my template. I'm getting some unexpected results.
{% for painting in paintings_list %}
<p>{% for item in painting.style.all %}
{{ item.style|join:', ' }}
{% endfor %}</p>
{% endfor %}
The contents are being displayed as they exist in the database, but they're displayed in an unanticipated way...ie. instead of:
Renaissance, Baroque, Expressionist
I'm getting:
R,e,n,a,i,s,s,a,n,c,e,,B,a,r,o,q,u,e,,E,x,p,r,e,s,s,i,o,n,i,s,t
Any idea what I'm doing wrong? Would have thought the join template filter was for exactly this type of scenario, but perhaps the proper way to do this would be to create a custom method of the model...
Ignacio Vasquez-Abrams is correct (as usual). A solution to your problem might lie in the forloop variables.
<p>{% for item in painting.style.all %}
{{item.style}} {% if not forloop.last %}, {% endif %}
{% endfor %}</p>
item.style is returning a string, so you're joining each character with , instead of each item.