I want to display my non_field_erors in my template. So far, I can display all kind of errors of my forms with:
-> base.html
{% if form.errors %}
{% for field in form %}
{% if field.errors %}
<div class="ui-state-error ui-corner-all notification" >
<p>
<span class="ui-icon ui-icon-alert"></span>
{{ field.label_tag }}:{{ field.errors|striptags }}
<a class="hide" onClick="hideBar(this)">hide</a>
</p>
</div>
{% endif %}
{% endfor%}
{% endif %}
AND
{{ form.non_field_errors }}
I've added a new form which has only an IntegerField:
class MerchantForm(forms.ModelForm):
price = forms.IntegerField(widget=forms.TextInput(attrs={'class':'small'}))
def clean_price(self):
price = self.cleaned_data.get('price')
if price == 120:
raise forms.ValidationError('error blah.')
return price
When I post price as 120, I don't get any validation errors in my page.
And my view is:
def bid(request,product_slug):
.
.
form = MerchantForm()
context = RequestContext(request,{
'form':form,
....
})
if request.method == 'POST':
form = MerchantForm(request.POST)
if form.is_valid():
return HttpResponse('ok')
# else:
# return HttpResponse(form.errors.get('__all__'))
return render_to_response('bid.html',context_instance=context)
I can retrieve the error with commented lines but I don't want to do that in views.py. Any ideas ?
Oh dear.
First of all, why are you asking about non_field_errors when the code snippet you post clearly has the error as being raised in clean_price, and therefore is associated with ths price field?
Secondly, your view code is upside down. You create an empty form instance, and add it to the context. Then you create another form instance, bound to the POST data, but don't put it into the context. So the template never sees the bound form, so naturally you never see any validation errors in the template.
Related
I am trying to raise ValidationError if a duplicate entry found in an inlineformset, just like in the docs: Custom formset validation I'm using a debugging tool and stepping through the code. It successfully reaches the line: raise ValidationError("Check Duplicate Entry"), and subsequently, in the view it takes the else statement instead proceeding with if formset.is_valid. I print(formset.non_form_errors) just as in the docs. However, the example in the docs returns a list at this stage with the error message inside it. In my case, I get <bound method BaseFormSet.non_form_errors of <django.forms.formsets.MyInlineFormSet object at 0x7fcc22e89610>> The template renders with the form data but the validation error does not appear to say what is wrong. I'm missing something for sure. Any help appreciated.
class MyInlineFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(MyInlineFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
def clean(self):
if any(self.errors):
return
mylist = []
for form in self.forms:
myfield_data = form.cleaned_data.get("myfield")
if myfield_data in mylist:
raise ValidationError("Check Duplicate Entry")
mylist.append(myfield_data)
My tempate just has this:
{% block content %}
<section class="container-fluid">
{% crispy form %}
</section>
{% endblock content %}
Side Note: Any field validation errors against specific fields do show up. But there I am writing the errors like so: self._errors["fieldname"] = self.error_class(["Error in field, please double check"]) Not sure why raise ValidationError is not working for the formset
SOLVED! My formset is a crsipy form within a crispy form. The overall form is rendered in the template linked to the view. But I should have mentioned, I have a separate template containing the markup for the formset.
<div class="row justify-content-center">
<div class="col-7">
{{ formset.management_form | crispy }}
{% for form in formset.forms %}
<h3>Form {{forloop.counter}}</h3>
{% crispy form %}
{% endfor %}
</div>
<br>
</div>
So I just needed to add {{ formset.non_form_errors }} below formset.management_form.
I have a page where there is a path /tag/name_of_tag and you can see all posts tagged with that tag.
Inside the page, you can also select another tag in a form and go to that tag.
The problem is that instead of going to /tag/searched_tag, it goes to /tag/?search=searched_tag
How can I change it doesn't leave the ?search= part?
urls.py:
url(r'tag/(?P<input_tag>\w+)$', views.tag_view, name='tag'),
views.py:
def tag_view(request, input_tag):
form = TagSearchForm()
if request.method == 'GET':
form = TagSearchForm(request.GET)
if form.is_valid():
input = form.cleaned_data['search']
print(input)
return redirect('fortykwords:tag_view', input)
else:
form = SearchForm()
latest_post_list = Post.objects.filter(tags=input_tag, status__exact="published")
paginator = Paginator(latest_post_list, 3)
page = request.GET.get('page')
posts = paginator.get_page(page)
context = {'latest_post_list': latest_post_list, 'page_tag': input_tag, 'form': form}
return render(request, 'fortykwords/tag.html', context)
forms.py:
class TagSearchForm(forms.Form):
search = tagulous.forms.SingleTagField(
tag_options=tagulous.models.TagOptions(
autocomplete_view='fortykwords:post_tags_autocomplete'
),
label='Tags',
required=True,
help_text=_('Filter by lead tags. You can organize leads by any tag you want.'),
)
tag.html:
{% extends "base_generic.html" %}
{% block content %}
<form action="." method="get">
{{ form }}
<input type="submit" value="Submit" />
</form>
<h3>Posts with the tag {{ page_tag }}</h3>
{% if latest_post_list %}
<ul>
{% for post in latest_post_list %}
<li> {{ post.author }} {{ post.pub_date }}
<br>
{{ post.title }}</li>
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}
</ul>
{% else %}
<p>No posts are available.</p>
{% endif %}
{% endblock %}
You need to provide the argument input to redirect method as input_tag=input.
Example:
return redirect('fortykwords:tag_view', input_tag=input)
It's showing as /tag/?search=searched_tag because your form is submitting by GET but never getting to the redirect. It seems is_valid() is returning False.
I've tested a very similar version of your code and don't think it's a bug in tagulous, but would still be interested to know what had gone wrong (I wrote tagulous). Spotted a couple of places you can streamline your code a bit, so try::
def tag_view(request, input_tag):
# Can't see any POSTs in your example, so you can pass the form GET here
# Might also be nice to pass the original tag in so it shows in the form
form = TagSearchForm(request.GET, initial={'search': input_tag})
# The form already has the GET, so you can go straight into the is_valid
if form.is_valid():
input = form.cleaned_data['search']
print('Valid: ', input)
return redirect('fortykwords:tag_view', input)
else:
print('Invalid: ', form.errors, form.non_field_errors)
# You can remove the else for if not GET, which would never be reached
# on to pagination as before
(although fwiw I'd recommend ipdb instead of print)
Problem description (Django 1.7):-
Simple form is rendered containing input fields and some template variables. The template variables are set in the supporting view and show some boilerplate legal text.
I have a custom "clean" method that checks the input field values and sets validation errors as needed.
If errors have occurred, the form is re-displayed with the correct error messages by the fields that are in error.
However, the template variables are lost, i.e.
{{ my-template-variable }} that when the form is initially rendered is OK is then not set when the form is re-rendered to show the errors.
I understand why this is happening, but can anyone advise how I can reset the template variable in the clean method so this works as expected?
Here's the code:-
views.py
def form1(request):
legal_text = "Some long legal paragraph ...."
if request.method == 'POST':
form = form1(request.POST)
# do stuff with the posted values ...
else:
form = form1()
return render(request,'form11.html', {'form': form, 'legal_text': legal_text})
forms.py
class form1(forms.Form):
# form contains lots of input questions 'q1', 'q2' etc that are cross-validated
def clean(self):
cleaned_data = super(form1,self).clean()
if not cleaned_data.get('q1'):
self._errors['q1'] = 'Please provide a selection'
if not cleaned_data.get('q2'):
self._errors['q2'] = 'Please provide a selection'
Q1_CHOICES = (('10', 'Yes',), ('01', 'No',),)
q1 = forms.ChoiceField(required=False,label="This is question 1",help_text="Some arbitrary help text",widget=forms.RadioSelect,choices=Q1_CHOICES)
Q2_CHOICES = (('10', 'Yes',), ('01', 'No',),)
q2 = forms.ChoiceField(required=False,label="This is question 1",help_text="Some arbitrary help text",widget=forms.RadioSelect,choices=Q2_CHOICES)
# more questions omitted ...
form1.html (snippet)
<html>
<body>
{{ legal_text }} <!-- this is NOT displayed properly when errors occur -->
<h4><strong>{{ form.q1.label }}</strong></h4>
{% if form.q1.errors %}
<div class="alert alert-danger" role="alert">{{form.q1.errors }}</div>
{% endif %}
<br/>
{{ form.q1.help_text }}
<br/><br/>
{{ form.q1 }}
<br/>
<h4><strong>{{ form.q2.label }}</strong></h4>
{% if form.q2.errors %}
<div class="alert alert-danger" role="alert">{{form.q2.errors }}</div>
{% endif %}
<br/>
{{ form.q2.help_text }}
<br/><br/>
{{ form.q2 }}
</body>
</html>
Here you are overriding the form clean method and not returning clean_data. Replace your code like
def clean(self):
cleaned_data = super(form1,self).clean()
if not cleaned_data.get('q1'):
self._errors['q1'] = 'Please provide a selection'
if not cleaned_data.get('q2'):
self._errors['q2'] = 'Please provide a selection'
return self.cleaned_data
I have a search page where user can submit queries that try to match objects in a database. When the user has submitted the form and no results have been found for his/her query, I want to give the message "No results found for this query". The problem I'm having now is that the message is shown even when the user has not submitted the form yet.
This is what my code looks like:
Review Template
<div> What do you want to review? </div>
<form action="/newreview/" method="get">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<input type="submit" value="Submit" name="submit" />
{% if results %}
{{ results.id }}
{% else %}
<li> There is no results for this search </li>
{% endif %}
</form>
And I do the following for the View:
def newreview(request):
if 'submit' in request.GET: # If the form has been submitted...
form = LookforPlace(request.GET) # A form bound to the GET data
if form.is_valid(): # All validation rules pass
name = form.cleaned_data['name']
city = form.cleaned_data['city']
try:
results = Place.objects.get(name__icontains=name)
except Place.DoesNotExist:
results = None
else:
results = []
form = LookforPlace() # An unbound form
return render_to_response('newreview.html', {
'form': form, 'results': results,
})
I thought that by doing the conditional on the results list, I could check whether the form has been submitted or not (empty list from the view).
Right now this code is giving me "There is no results for this search" even if there has not been any submission. Any thoughts?
Why not put a flag in your context to determine whether or not it has been submitted?
if 'submit' in request.GET:
submitted = True
...
else:
submitted = False
...
{% if submitted %}
{% if results %}
{{ results.id }}
{% else %}
<li> There are no results for this search </li>
{% endif %}
{% endif %}
now im learning to validate form, "all" is working, im showing the erros of empty fields, but i have 2 questions:
how ill show the value in the filled fields when there are errors in another fields?, like <input ... value= {{ value }} > the problem is that my fields are not html forms fields.
how ill show the error exactly over the empty fields?
how i have this:
form.py
class NuevaDiligenciaForm(forms.Form):
titulo = forms.CharField(max_length=70)
tipo = forms.ChoiceField(choices=TIPO)
vias= forms.TypedChoiceField(widget=forms.RadioSelect(), choices=CHOICES)
view.py
def add(request):
form = NuevaDiligenciaForm()
errors =[]
if request.method =='POST':
if not request.POST.get('titulo',''):
errors.append('Titulo es requerido')
if not request.POST.get('vias',''):
errors.append('Vias es requerido')
#if not errors:
return render_to_response('account/add.html', { 'formulario':form ,'errors':errors},context_instance = RequestContext(request))
template.html
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% if message %}
<p style="color: red;">{{ message }}</p>
{% endif %}
<form action='.' method='POST' class="nueva-diligencia">
{{ formulario.as_p }}
<input type="submit" value="Continuar">
</form>
Thanks again :)
You form code looks fine here but your view needs to change to this:
def add(request):
if request.method =='POST':
form = NuevaDiligenciaForm(request.POST)
if form.is_valid():
clean_data = form.cleaned_data
# Now do something with the cleaned data...
else:
form = NuevaDiligenciaForm()
return render_to_response('account/add.html', { 'formulario': form }
and your template should look like this:
{% if message %}
<p style="color: red;">{{ message }}</p>
{% endif %}
<form action='.' method='POST' class="nueva-diligencia">
{{ formulario.as_p }}
<input type="submit" value="Continuar">
</form>
Now what happens is that if there is bad data from the POST, form.is_valid() will fail and the view will return the validated form, which will include errors next to fields that have them. Django takes care of all the error handling for you here! Try it out and let me know if it works as you expect it to.
This is a pretty good resource if you'd like to see how/why this simplified version actually works better: http://www.djangobook.com/en/2.0/chapter07/