POST data has wrong value for Django template variable? - python

I have the following code in my template:
<form action="" method="post">{% csrf_token %}
{%for category, category_votes in votes%}
<p>{{category}}: {{category_votes}} <!-- displays as expected -->
<input type="hidden" name="votedCat" value="{{category}}" id={{forloop.counter}}>
<input type="submit" name="upvote" value="Vote for...">
<input type="submit" name="downvote" value="Vote against...">
</p>
{%endfor%}
</form>
The variable {{category}} displays as expected when rendered, but looking in the POST data, "votedCat" is always the last category value in votes.
For example, if votes=[('a',1),('b',2),('c',3)], then request.POST['votedCat'] returns 'c' regardless of which input button is used to submit the form. What did I do wrong?

Because you only have one single form, with multiple inputs for votedCat. Clicking any of the buttons submits the whole form, with all the values for votedCat. If you were to access request.POST.getlist('votedCat') you would see that you actually have all the values.
There are two ways of fixing this. The first is to have separate form elements for each iteration through the loop - to do that, just move the <form> and </form> elements inside the loop.
The second is to have the votedCat input actually be the submit button:
<input type="submit" name="votedCat" value="Vote for {{category}}" id={{forloop.counter}}>
The disadvantage here is that now you have the words 'Vote for' in your variable, which you'll need to parse out in the view code.
Better than both of these would be to have a simple radio button set or select box with a single submit button, but I understand that design requirements sometimes get in the way.
Finally, you should really be using Django's forms framework, rather than using manual HTML and dealing with the POST directly.

I'm not sure if this is the best solution, but you can create a new form inside the loop:
{%for category, category_votes in votes%}
<p>{{category}}: {{category_votes}} <!-- displays as expected -->
<form action="" method="post">{% csrf_token %}
<input type="hidden" name="votedCat" value="{{category}}" id={{forloop.counter}}>
<input type="submit" name="upvote" value="Vote for...">
<input type="submit" name="downvote" value="Vote against...">
</form>
</p>
{%endfor%}
You could consider using the django.forms.Form class to build and process your forms.

Related

How to append two separate forms to the end of a url in Django

I am attempting to create a django web app, and I'm running into an issue with forms. I have a simple index.html set up that has two separate regular html forms on the page. One for filtering and the other for sorting. Currently, I can get either filtering, or sorting to work, but not both at the same time. I think this is because I'm missing a fundamental understanding of django somewhere. Yes, I've done the tutorials.
I've attempted manually appending the URL to my url config, but that didn't work as intended.
<form action="" method="get">
{% for filter in view.all_filters %}
<label>
<input type="checkbox" name="filter" value="{{ filter }}">
{{ filter }}
</label>
{% endfor %}
<input type="submit" value="Apply Filters">
</form>
<form action="." method="get">
<label>
Sort By
<select name="order_by">
<option value="name">Name</option>
<option value="description">Description</option>
<option value="cvssBaseScore">Cvss Base Score</option>
<option value="cvssV3BaseScore">Cvss V3 Base Score</option>
</select>
</label>
<input type="submit" value="Submit">
</form>
I would like the url to be able to append something like ?filters=one&filters=two&order_by=name or something as such.
The question is not really related to Django but rather to how HTML forms work:
A <form> element encapsulates all the data (<input>s) that it submits, so if you put two separate forms in your HTML page, then you intentionally want only one of the forms to be submitted with its data.
Just put both your inputs (the filter and the order_by inputs) inside the same <form> element so that both are submitted when either of the submit buttons is clicked.

Django form input and action linking

How do you pass form inputs to a form action in Django? I tried this but it's not working
<form action="/search?search_term=q" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
I'm assuming you're asking how to link an HTML form to a view function for processing. Note that in Django, we don't call them actions.
Here's the basic form and placeholder for the search results that you'd put in a Django template:
<form action="/search" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
{% if search_results %}
Display search results here.
{% endif %}
If you type apple into the text field and then click the search button on this form it will make a GET request to /search?q=apple.
You will need to route the request via a URLconf to a view function to do the processing.
The entry in the URLconf may look like the following:
from django.conf.urls import url
from app import views
urlpatterns = [
# ...
url(r'^search/$', views.search),
# ...
]
views.search is the view function that does the processing. Here's a possible implementation:
from django.shortcuts import render
def search(request):
q = request.GET.get('q')
if q is None:
return render(request, 'app/search.html')
search_results = perform_search(q)
context = { 'search_results': search_results }
return render(request, 'app/search.html', context)
Where perform_search is a function you'd need to write to do the actual searching.
When you specify a method GET in form, then You don't need to append parameters explicitly in the URL. When the user submits the form your parameter will automatically append in the URL as key-value pairs.
Example
<form action="search" method="GET">
<input type="text" name="search_item1">
<input type="text" name="search_item2">
<input type="submit" value="Search">
</form>
When a user hit submit button then your parameter(serach_item1 and serach_item2) will append implicitly in action URL. where keys are input fields name attribute
Now your URL looks like
www.example.com/search?search_item1=<serach1>&search_item2=<serach2>
To pass GET arguments to the page you don't need to specify it in action attribute.
Just do follow:
<form method="GET" action="search/">
<input type="text" name="search_term" value="{{ request.GET.search_term }}">
<input type="submit" value="Search">
</form>
this will redirect you to the page http://yourdomain.com/search?search_term=search_text.
Please note I replaced input name attribute to rename GET argument to search_term. Also I added value attribute to display current search text to the input field.
shuboy2014 and neverwalkaloner are correct. But a fact they didn't tell you is that you'll pass empty values if you declare no values in that fields. For example:
<form method="GET" action="search/">
<input type="text" name="search_term">
<input type="submit" value="Search">
</form>
That would create an URL like http://yourdomain.com/search?search_term=, which could lead to unexpected errors in the View later. You either should do request.GET.get("search_term") to avoid exceptions or modify the submit function of your form to dinamically remove the names of those empty fields.

Django change site language

I am using Django and I want to change the site language when users click a button for example. I can get the current language code using {% get_current_language as LANGUAGE_CODE %} but how can I change it from a template?
<form name="setLangEnglish" action="/i18n/setlang/" method="POST">
{% csrf_token %}
<input name="next" type="hidden" value="{{request.path}}"/>
<input type="hidden" name="language" value="en"/>
</form>
What this form actually does is changing your current language to "en" which is english.Since this form input type is hidden,you wouldn't see form in html. What you have to do is find out how you would like to change language(with clicking text or clicking country flag.).Here is an example for changing langugage with clicking text.
<a onclick="document.setLangEnglish.submit();return false;">ENG</a>
This code submit the form above whenever user clicks "ENG".In order to be this working, make sure that url(r'^i18n/',include('django.conf.urls.i18n')), is in the urls.py.

How can you manually render a form field with its initial value set?

I'm trying to render a form's fields manually so that my designer colleagues could manipulate the input elements within the HTML instead of struggling in Python source.
ie. Instead of declaring form fields like this...
{{ form.first_name }}
.. I actually do ...
<label for="id_first_name">Your name:</label>
<input type="text" value="{{ form.first_name.somehow_get_initial_value_here }}" name="first_name" id="id_first_name" class="blabla" maxlength="30" >
{% if form.first_name.errors %}<span>*** {{ form.first_name.errors|join:", " }}</span>{% endif %}
Problem is that there seems to be no way to get the initial value of the field in the second method. {{ form.first_name }} statement does render the input element with the proper initial value but somehow there is nothing like {{ form.first_name.initial_value }} field when you want to render the form manually.
There is an interesting long standing ticket about this very issue. There is sample code to implement a template filter in the comments that should do what you need:
http://code.djangoproject.com/ticket/10427
<input value="{{form.name.data|default_if_none:'my_defaut_value'}}" ... />
You will have to use default_if_none because the implicit value of a bound field will not be stored in data. More details here

How to process two forms in one view?

I have two completely different forms in one template. How to process them in one view? How can I distinguish which of the forms was submitted? How can I use prefix to acomplish that? Or maybe it's better to write separate views?
regards
chriss
Personally, I'd use one view to handle each form's POST.
On the other hand, you could use a hidden input element that indicate which form was used
<form action="/blog/" method="POST">
{{ blog_form.as_p }}
<input type="hidden" name="form-type" value"blog-form" /> <!-- set type -->
<input type="submit" value="Submit" />
</form>
...
<form action="/blog/" method="POST">
{{ micro_form.as_p }}
<input type="hidden" name="form-type" value"micro-form" /> <!-- set type -->
<input type="submit" value="Submit" />
</form>
With a view like:
def blog(request):
if request.method == 'POST':
if request.POST['form-type'] == u"blog-form": # test the form type
form = BlogForm(request.POST)
...
else:
form = MicroForm(request.POST)
...
return render_to_response('blog.html', {
'blog_form': BlogForm(),
'micro_form': MicroForm(),
})
... but once again, I think one view per form (even if the view only accepts POSTs) is simpler than trying to do the above.
like ayaz said, you should give unique name to form submit button
<form action="." method="post">
......
<input type="submit" name="form1">
</form>
<form action="." method="post">
......
<input type="submit" name="form2">
</form>
#view
if "form1" in request.POST:
...
if "form2" in request.POST:
...
If the two forms are completely different, it will certainly not hurt to have them be handled by two different views. Otherwise, you may use the 'hidden input element' trick zacherates has touched upon. Or, you could always give each submit element a unique name, and differentiate in the view which form was submitted based on that.

Categories