Django Forms - getting data from multiple choice fields - django

I have a form like so:
class MyForm(forms.Form):
site = forms.ChoiceField(choices=SITE_CHOICES, label=ugettext_lazy('Site'),)
...
params = forms.MultipleChoiceField(
choices=PARAM_CHOICES,
label=ugettext_lazy('Select Parameters'),
widget=forms.CheckboxSelectMultiple()
)
And in my template:
<form action="{% url results %}" method="get">{% csrf_token %}
{% for field in myform %}
<div class="field_wrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
<input type="submit" name="submit" value="{% trans 'Query' %}" />
</form>
My problem is that when I submit the form as GET the variables are like the following:
site=1&params=foo&params=bar&params=something&submit=Query
my params variable is clearly being overwritten by the last choice? How can get access to the submitted data as separate variables?
Any help appreciated.

Using Django forms
You should be using Django's form handling with a POST which would majke things easier. Here goes:
if request.method == 'GET':
form = MyFormClass()
else:
form = MyFormClass(request.POST)
if form.is_valid():
do_something_with(form.cleaned_data['params'])
return redirect('somewhere')
return render_to_response('my_template.html', RequestContext(request, {'form':form}))
Notes on using GET vs POST with forms
It's useless to include {% csrf_token %} if you're going to GET the form (Absolutely no csrf validation is done with GET requests, which make sense, as GET requests are supposed to be non-data-altering.
Anyway, if you're really going to GET the page, you can still use the same logic as written before, with a little tuning:
form = MyFormClass(request.GET)
if form.is_valid():
do_something_with(form.cleaned_data['params'])
return render_to_response('some_template.html', {'stuff':some_stuff})
return render_to_response('form_submission_page.html', {'form':form})
Last thing, using GET to submit data is usually bad practice, unless you're creating some search function or altering display (pagination & all).
Using request.GET
Now, if for some reason you don't want to use Django forms, you can still get around the problem and retrieve your params, you simply need to use the QueryDict.getlist instead of using the QueryDict.get method.
Here goes:
my_data = request.GET.getlist('params')
Documentation
Don't forget to check out the Django documentation on QueryDicts and on forms

And use {% csrf_token %} in get request is a bad practice.

Use form.is_valid() and form.cleaned_data['params'].

Related

Django template language syntax

I'm learning django and i'm blocked on a template syntax error.
I have this function in my views.py :
def AccountUpdateView(request):
template_name='portal/accountupdate.html'
context = {"forms":UserForm}
return render(request,template_name,context)
There is my template :
<form action="/account/update/" method="POST">
<ul>
{% csrf_token %}
{% for form in forms %}
<li>{{form.label}}
<input type="text" name="{{form.name}}" maxlength="32" required="" id="id_{{form.name}}" value="{{PLEASE HELP ME !!!}}">
</li>
{%endfor%}
</ul>
<input type="submit" value="Metre a jour" />
</form>
Well, i'm trying to get in the "value" of each form on my template by the current registered user known in django by the call {{user}}
And i would to auto place the values of each forms.
I think a solution is to use the form.name (for the example of the case 'username') and in the value call a thing like this :
user.form.username
It doesn't work and i know that i was dream to hope this exotic call don't work...
If any have a solution :)
Thank's you !
You shouldn't do this at all. Django will automatically output the whole field if you ask it.
For a start, use proper names for your objects. Secondly, if you want to prepopulate the form with data from the current user, then do so in the view. Note, you also need to deal with the posted data:
def AccountUpdateView(request):
template_name='portal/accountupdate.html'
if request.method == 'POST':
form = UserForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect('/')
else:
form = UserForm(instance=request.user)
context = {"form":form}
return render(request,template_name,context)
Now, use the proper values and attributes in the template:
{% for field in form %}
<li>{{ field.label_tag }}
{{ field }}
{{ field.errors }}</li>
{% endfor %}

Create Django dropdown form which allows users to select a model to submit

Extreme noob here. I have been trying to create a simple form in Django where the user can select from the models that are present in the database and then click submit (whereupon I will then return a list of objects).
However, I am getting the following error: 'ModelBase' object is not iterable
Note all I am trying to achieve so far is to actually render the form.
Here is the code:
HTML
<form action="." method="">
{% csrf_token %}
{% for field in output %}
{{ output.as_p }}
{% endfor %}
<input type="submit" value="Save" />
</form>
Forms.py
from projectdb.models import *
class TablesForm(forms.Form):
models = models.get_models()
select = forms.ChoiceField(choices=models)
Views.py
def output_form(request):
form = TablesForm()
return render(request, 'projectdb/output.html', {'output': form})
Much obliged for any help!
Some object is not iterable errors will be fixed by adding .all() where you're doing the foreach loop. If the loop is in the template, try .all only
In a view:
for something in array.all():
In a template:
{% for field in output.all %}
And everytime I do a form in Django my method on the form is empty method="". This let's you return to the same view and process your data there. If you have errors on the form you can redirect to the same form but with the pre-saved data that the user has wrote.
Hope it helps.

loop over forms in formset and a list at same time + FileField required error :django

I have a formset which i initialize it in view. one of the form's fields is FileField. and I have to show user the name of his/her previous file name. Because i can't initialize FileField, i want to send file names in list. (I mean for example when you have a Charfield, you can initialize it in views and when you render to template, you will see an input filled with that data, but when you have file upload field you can't filled it in views and sends to template). i don't know how i can loop over forms in formset and list at the same time. And the other thing is when i initialize forms in formset and render to template (I mean for example write data['form-0-Team']='team1' but i can't write data['form-0-Team']='a.png' , so when i render to template, i see field named 'Team' is filled (value=team1) and field named 'File' is not filled and the error 'thid field is required' id shown. ) although it's the first time i'm visiting this page and my method isn't POST. (USUALLY form errors are shown when user clicks on submit and in views it checks if request.method == 'POST', then checks if form.is_valid, it return to template and shows errors, but in mine, it shows errors at the first time euser is visiting the page and before he/she posts data).
I wish i could say my problem. can you please guide me solve this? really thanks.
def myFunc(request):
flagFormSet = formset_factory(FlagFileBaseForm)
if request.method == 'POST':
formset = flagFormSet(request.POST, request.FILES)
if formset.is_valid():
# do s.th
else:
data = {
'form-TOTAL_FORMS': 5,
'form-INITIAL_FORMS': u'0',
'form-MAX_NUM_FORMS': u'',
# add initial form data to it
}
list=['a.png', 'b.png', 'c.png', 'd.png' , 'f.png']
formset = flagFormSet(data)
return render_to_response('myPage.html', RequestContext(request, { 'formset': formset, 'list':list}))
and my template:
<form method="post" action="" enctype="multipart/form-data">
{{ formset.management_form }}
{% for form in formset.forms %}
<div class="form">
<div class="form-row Team">
<div>
<label class="required" for="id_Team">Team:</label>
{{ form.Team }}
{{ form.Team.errors }}
</div>
</div>
<div class="form-row File">
<div>
<label class="required" for="id_File">File:</label>
{{ form.File }}
{{ form.File.errors }}
</div>
#here i want show the name of previous file
</div>
</div>
{% endfor %}
</form>
EDIT:
current result (while request is not post, it shows error)
desired result (form without error with file names)
In Django Templates, if you are trying to render a form that has a FileField. You must pass replace
<form method="post" action="">
with
<form method="POST" enctype="multipart/form-data">
https://docs.djangoproject.com/en/dev/ref/forms/api/#binding-uploaded-files-to-a-form
I'm afraid I don't really understand your question, so apologies if this doesn't help..
If your list is arbitary (as it looks from your question), then you could use django's built in forloop counter to construct your file names -
{% for form in formset %}
{{ form }}
<input type="text" name="file_name" value="{% forloop.counter %}.png">
{% endfor %}
Alternatively have a look at python's zip function. You could use it to build an object that includes both the names and the forms and pass that to your template.

Django modelformset_factory invalid, return request.POST data to forms

I want to return the data that has been entered back in place in the form using django modelformset_factory
view
from django.forms.models import modelformset_factory
ArticleFormSet = modelformset_factory(Article, extra=2)
formset = ArticleFormSet(queryset=Article.objects.none())
if request.POST:
formset = ArticleFormSet(request.POST)
if formset.is_valid():
instances = formset.save(commit=False)
formset = ArticleFormSet(queryset=instances)
else:
## ?? RETURN formset with request.POST data in form ??
pass
template
<form method="POST" id="articleForm" action=".">{% csrf_token %}
{% for form in forms %}
{{form.as_p}}
{% endfor %}
</form>
Using Django 1.4 I'm getting:
Validation Error: No exception supplied
Have tried a range for variations on initial=request.POST, but no joy.
Any advice is appreciated thank you!
You don't need to do anything. Your formset is already populated with the POST data. Just pass that to the template. The usual way to do this is not to have an else clause, but just fall through to the same render call that displayed the forms in the first place.
Checked to see if the "management form" details were not being automatically passed, turns out they weren't (per here: https://docs.djangoproject.com/en/dev/topics/forms/formsets/#understanding-the-managementform).
Cobbling this "Management Form data" on to request.POST seems to have resolved the otherwise mysterious validation error. I wonder if this a genuine 1.4 bug?
if request.POST:
forms_mgmt = {'form-TOTAL_FORMS': u'2', 'form-INITIAL_FORMS': u'0', 'form-MAX_NUM_FORMS': u''}
data_dict = dict(request.POST.items() + forms_mgmt.items())
formset = ArticleFormSet(data_dict)
if formset.is_valid():
instances = formset.save(commit=False)
formset = ArticleFormSet(queryset=instances)
Thanks to #Daniel Roseman for helping me not go down the wrong path.
Further to #Williams answer, reading through https://docs.djangoproject.com/en/dev/topics/forms/formsets/#understanding-the-managementform you can find that the management form data that can be rendered into the template with {{ my_formset.management_form }}:
<form method="POST" id="articleForm" action=".">{% csrf_token %}
{% for form in forms %}
{{form.as_p}}
{% endfor %}
{{ my_formset.management_form }}
</form>
Then, once the form has been posted you can use:
formset = ArticleFormSet(request.POST)

Django form validation with GET

I have a form which allows users to select several parameters to allow faceted querying of data. As there is no data entry going on here I want the form to post to GET and I have a another view with a different template which displays the results.
I want the form to validate as normal so that if a required field is not completed the corresponding errors are displayed. At the moment my process looks like this (simplified):
my search view:
def search(request):
...
context['form'] = GraphForm()
...
return render(request, 'search.html', context)
my results view:
def results(request):
if 'submit' in request.GET:
# process GET variables as query
...
return render(request, 'results.html', context)
my search.html template:
<form action="{% url results %}" method="get">{% csrf_token %}
{% for field in form %}
<div class="field_wrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
<input type="submit" name="submit" value="Query" />
</form>
Given that the form submits to another url with separate view code, what is the best way to go about validating (highlighting errors), and ensuring I have my GET data?
Any help much appreciated.
This might be a little late, but I think the following will work while maintaining similarity to 'POST' workflow:
Instead of having two different views for searching and displaying results, just have one view. The normal codepath described for post forms can then be followed. Instead of using request.method == 'POST' to detect form submission, we instead use 'submit' in request.GET. If using javascript to submit the form, make sure that 'submit' is included in the GET data or use a hidden field to detect form submission.
views.py
def search(request):
context_dict = {}
if 'submit' in request.GET:
form = GraphForm(request.GET)
if form.is_valid():
#do search and add results to context
#If you don't want to use a single view,
# you would redirect to results view here.
results = get_results(**form.cleaned_date)
context_dict['results'] = results
else:
form = GraphForm()
context_dict['form'] = form
return render(request, 'search.html', context_dict)
search.html
<form action="{% url 'search' %}" method="get">
{{form}}
<input type="submit" name="submit" value="Query" />
</form>
{% if results %}
{% include 'results.html' %}
{% endif %}
You should be able to pass request.GET just like request.POST to the form. The form simply accepts a data dictionary. It doesn't care where that comes from. Have you already tried that?
Use JavaScript/jQuery for form validation. All you need to do is add an id to form, and in the corresponding Javascript, do something like
document.getElementById("#form").onsubmit = checkForm();
or using jQuery
$("#form").submit(checkForm);
where checkForm() returns true upon successful validation, and false otherwise. (Note that, if you do not return false, form submission will continue as usual.)
Which fields you check for/validate can also change by using Django's templates.