I have a form with only one field which is a MultipleChoiceField. In the template it is being printed with two other forms that are ModelForms inside the same HTML form (as described here).
When reading all of the POST data in the view, everything is there and working correctly except the values from this MultipleChoiceField, which is shown only the last selected value from the form if selecting it straight from request.POST['field'] -- but interesting enough, if I print request.POST, everything selected is there. How is this possible? This is really puzzling my mind.
This is the form:
class EstadosAtendidosForm(forms.Form):
estadosSelecionados = forms.MultipleChoiceField(choices = choices.UF.list)
This is the view:
#login_required
#csrf_protect
def cadastro_transportadora(request):
if request.method == 'POST':
print request.POST
print len(request.POST['estadosSelecionados'])
print request.POST
estadosSelecionados = request.POST['estadosSelecionados']
for estado in estadosSelecionados:
print estado
form_end = EnderecoForm(request.POST)
form_transp = TransportadoraForm(request.POST)
else:
transportadora_form = TransportadoraForm()
endereco_form = EnderecoForm()
estados_form = EstadosAtendidosForm()
return render(request, 'transporte/transportadora/cadastro.html', {'transportadora_form': transportadora_form, 'endereco_form': endereco_form, 'estados_form': estados_form})
And this is the template:
{% extends "transporte/base.html" %}
{% block main %}
<h1>Cadastro de Transportadora</h1>
<form enctype="multipart/form-data" action="" method="POST">
{% csrf_token %}
<h4>Dados da transportadora</h4>
{{ transportadora_form.as_p }}
<h4>Endereço</h4>
{{ endereco_form.as_p }}
<h4>Estados atendidos</h4>
{{ estados_form.as_p }}
<input type="submit" />
</form>
{% endblock %}
The output from the prints in the view, since line 5 to 10, is as follows:
<QueryDict: {u'nome': [u'Test name'], u'bairro': [u'Estrela'], u'logradouro': [u'R. SHudhsu'], u'numero': [u'234'], u'estadosSelecionados': [u'AM', u'RJ', u'SP'], u'telefone': [u'+559965321232'], u'cep': [u'88088888'], u'csrfmiddlewaretoken': [u'mQhxZlbosISw4acZOmTWw6FpaQPwg2lJ'], u'estado': [u'AM'], u'email': [u'test#email.com']}>
2
<QueryDict: {u'nome': [u'Test name'], u'bairro': [u'Estrela'], u'logradouro': [u'R. SHudhsu'], u'numero': [u'234'], u'estadosSelecionados': [u'AM', u'RJ', u'SP'], u'telefone': [u'+559965321232'], u'cep': [u'88088888'], u'csrfmiddlewaretoken': [u'mQhxZlbosISw4acZOmTWw6FpaQPwg2lJ'], u'estado': [u'AM'], u'email': [u'test#email.com']}>
S
P
See that the variable estadosSelecionados really contains the 3 values that I selected from the form, correctly, as a list, when I print the whole request.POST data, but when I print just request.POST['estadosSelecionados'], it doesn't.
Why?
POST is a QueryDict object, which has special behavior when multiple values are submitted in the HTTP POST for the same key. To get all of them, use the getlist method. Alternatively, just use your form - the form field will collect the multiple values for you.
You shouldn't be looking in request.POST. The whole point of using a form is that it takes care of things like type conversion. Look in form.cleaned_data['estadosSelecionados'] after checking form.is_valid().
Related
I am working on an Invoice that has three forms of which one has multiple instances. The first two forms each has one instance and the last one can accommodate as many as the user wants. meaning the user will be adding instances of the last form using JavaScript. With the below code I am getting an error that is saying that the fields of the first two forms are missing data cause whenever the user add extra fields to the last form Django thinks I am also adding extra forms to the other two forms. Please help
This is an example of the POST data with two extra fields for the last form:
form-TOTAL_FORMS '2'
form-INITIAL_FORMS '0'
form-MIN_NUM_FORMS '0'
form-MAX_NUM_FORMS '1000'
form-0-number '215'
form-0-customer '4'
form-0-sales_person '1'
form-0-product '2'
form-0-quantity '20'
form-0-price '20'
form-1-product '2'
form-1-quantity '40'
form-1-price '50'
This is my view.py
def createInvoice(request):
invoiceFormSet = formset_factory(InvoiceForm)
retailPriceFormSet = formset_factory(RetailPriceForm, extra=2)
saleItemFormSet = formset_factory(SaleItemForm)
if request.method == 'POST':
invoiceform = invoiceFormSet(request.POST or None)
retailPriceform = retailPriceFormSet(request.POST or None)
saleItemform = saleItemFormSet(request.POST or None)
if invoiceform.is_valid() and saleItemform.is_valid():
for si in saleItemform:
saleItem = si.save()
saleItem_id = get_object_or_404(SaleItem, id=saleItem.id)
for i in invoiceform:
inv = i.save(commit=False)
inv.sale_item = saleItem_id
inv.save()
for rp in retailPriceform:
retailPrice = rp.save(commit=False)
retailPrice.sale_item = saleItem_id
retailPrice.save()
return HttpResponse(retailPriceform.cleaned_data)
context = {'invoiceFormSet':invoiceFormSet,'retailPriceFormSet':retailPriceFormSet, 'saleItemFormSet':saleItemFormSet}
return render(request, 'createInvoice.html',context)
This is my template
{% extends 'base.html' %}
{% block title %} Create Invoice {% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{saleItemFormSet.as_p}}
{{invoiceFormSet.as_p}}
{{ retailPriceFormSet.management_form }}
{% for form in retailPriceFormSet %}
<div>{{form.as_p}}</div>
{% endfor %}
<button type="submit" class="btn btn-normal btn-main">Save</button>
</form>
{% endblock %}
Use a prefix to use multiple formset in a single view.
Example:
if get:,
article_formset = invoiceFormSet(prefix='articles')
book_formset = retailPriceFormSet(prefix='books')
if post:
article_formset = invoiceFormSet(request.POST, request.FILES, prefix='articles')
book_formset = retailPriceFormSet(request.POST, request.FILES, prefix='books')
reference: Django Documentation
My form has initial values in it. I use form.as_hidden to hide the values and pass those values through a POST request. However, the hidden values are not passing through. Is there a way through this?
views.py
def car_detail_view(request, id):
if request.method == "POST":
form = CarForm(request.POST)
print(form.is_valid())
if form.is_valid():
car_save = form.instance
get_car = Car.objects.get(number_plate=car_save.number_plate)
get_car.available = False
get_car.save()
return redirect('/')
else:
print(form.errors)
else:
car = Car.objects.get(id=id)
form = CarForm(initial={'brand':car.brand, 'number_plate':car.number_plate, 'price':car.price,
'available':car.available})
args = {
'car':car,
'form':form
}
return render(request, 'map/confirmation.html', args)
confirmation.html
<h1>Confirmation of Booking</h1>
{% block content %}
<p>Brand: {{ car.brand }}</p>
<p>Number Plate: {{ car.number_plate }}</p>
<p>Price: {{ car.price }}</p>
<p> Are you sure you want to book? <p>
<form class="" method="post">
{% csrf_token %}
{{ form.as_hidden }}
<input type="submit" value="Book {{ car.brand }}">
</form>
{% endblock %}
Error
<ul class="errorlist"><li>brand<ul class="errorlist"><li>This field is required.</li></ul></li><li>number_plate<ul class="errorlist"><li>This field is required.</li></ul></li><li>price<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Django doesn't have a form.as_hidden method. Therefore {{ form.as_hidden }} will render as the empty string '' in your template.
You can use the as_hidden method for individual form fields.
{{ form.number_plate.as_hidden }}
If you use values from hidden fields, you might need to add code to prevent the user altering the field values (e.g. with their browser's developer tools). However, in your case you don't need to get the values from the form, you can fetch them from the database.
def car_detail_view(request, id):
if request.method == "POST":
car = Car.objects.get(id=id)
car.available = False
car.save()
return redirect('/')
else:
car = Car.objects.get(id=id)
args = {
'car':car,
}
return render(request, 'map/confirmation.html', args)
Once you've got this working, you might want to think about what happens if two users try to book the same car at once.
I have a Flask application with Flask-SQLAlchemy and Flask-Admin.
I would like to perform a batch action, but with form. For example I would like to set a same text value to the form attributed, but this value is entered by the user.
I saw documentation for batch actions, but it has example only for predefined data.
Is it possible, or maybe there's some workaround for this?
The way I achieve this is to do an internal POST in the #action method.
class AddressView(AdminView):
# ... ....
#action('merge', 'Merge', 'Are you sure you want to merge selected addresses?')
def action_merge(self, ids):
if len(ids) < 2:
flash("Two or more addresses need to be selected before merging")
return
return_url = request.referrer
return redirect(url_for('mergeaddresses.index', return_url=return_url), code=307)
Then define two routes, one for the internal post and then one to receive the submit POST from the form that receives the user's input data (MergeAddressForm in the case below).
In the example below I happen to be using a Flask-Admin BaseView to handle the routes. Note how the original checked IDs in the flask-admin list view are retrieved and then stored in the form as an encoded comma delimited list hidden field and likewise the return_url back to the flask-admin list view.
class MergeAddressesView(BaseView):
form_base_class = BaseForm
def __init__(self, name=None, category=None,
endpoint=None, url=None,
template='admin/index.html',
menu_class_name=None,
menu_icon_type=None,
menu_icon_value=None):
super(MergeAddressesView, self).__init__(name,
category,
endpoint,
url or '/admin',
'static',
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._template = template
def is_visible(self):
return False
#expose('/', methods=['POST'])
def index(self):
if request.method == 'POST':
# get the original checked id's
ids = request.form.getlist('rowid')
merge_form = MergeAddressForm()
merge_form.process()
joined_ids = ','.join(ids)
encoded = base64.b64encode(joined_ids)
merge_form.ids.data = encoded
_return_url = request.args['return_url']
merge_form.return_url.data = _return_url
self._template_args['form'] = merge_form
self._template_args['cancel_url'] = _return_url
return self.render(self._template)
#expose('/merge', methods=['POST'])
def merge(self):
if request.method == 'POST':
merge_form = MergeAddressForm(selection_choices=[])
decoded = base64.b64decode(merge_form.ids.data)
ids = decoded.split(',')
# do the merging
address_merger = AddressMerger(ids=ids, primary_id=merge_form.primary_address.data)
address_merger.merge()
# return to the original flask-admin list view
return redirect(merge_form.return_url.data)
My template for the user input form looks something like below. Note the action url.
{% extends 'admin/base.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block body %}
<h3>{{ admin_view.name|title }}</h3>
<form role="form" action="{{ url_for('mergeaddresses.merge') }}" method="post" name="form">
{{ form.hidden_tag() }}
{{ wtf.form_errors(form) }}
{{ wtf.form_field(form.primary_address) }}
<button type="submit" class="btn btn-primary">Merge</button>
Cancel
</form>
{% endblock %}
My Django learning has brought me to Forms. I've been able to create a simple form, using the information from the book I'm reading. I've also create a form based on the Model I have created. The issue I am having is that I am trying to create my own formatting within the template and for some reason the label information isn't held within the formset. I'm a little confused at how using the default way of displaying this i.e. {{ form }} has this information.
What I have;
adminforms.py
class NewsForm(ModelForm):
class Meta:
model = News_Article
exclude = ('news_datetime_submitted', 'news_yearmonth', )
labels = {
'news_title': _('Enter News Title'),
}
help_texts = {
'news_title': _('Enter a title to give a short description of what the news is.'),
}
error_messages = {
'news_title': {
'max_length': _("News title is too long."),
},
}
view.py
def create(request, dataset):
if dataset not in ['news', 'announcement']:
# change this to the siteadmin page if authenticated and have permissions, otherwise go to home
return HttpResponseRedirect(reverse('pages'))
rDict = {}
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
if dataset == "news":
form = NewsForm(request.POST)
elif dataset == "announcement":
form = AnnouncementForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/home/')
else:
pass
# if a GET (or any other method) we'll create a blank form
else:
announcement = get_announcement()
if not announcement == None:
rDict['announcement'] = announcement
if dataset == "news":
rDict['formset'] = NewsForm()
rDict['branding'] = {'heading': 'Create News Item', 'breadcrumb': 'Create News', 'dataset': 'create/' + dataset + '/'}
elif dataset == "announcement":
rDict['form'] = AnnouncementForm()
rDict['branding'] = {'heading': 'Create Announcement', 'breadcrumb': 'Create Announcement', 'dataset': 'create/' + dataset + '/'}
rDict['sitenav'] = clean_url(request.path, ['"', "'"])
rDict['menu'] = Menu.objects.all().order_by('menu_position')
pdb.set_trace()
return render(request, 'en/public/admin/admin_create.html', rDict)
template
<form action="/siteadmin/{{ branding.dataset }}" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{% for field in form %}
{{ field.label_tag }} {{ field }}
{% endfor %}
{% endfor %}
<input type="submit" value="Submit" />
</form>
For some reason jut get the form fields and no label information. N.b. The template text I've gottom from the Django documentation on formsets.
I've taken a look at the data thats returned and no label information is in it, yet it works with just {{ form }} ? Bit confused.
(Pdb) rDict['formset']
<NewsForm bound=False, valid=Unknown, fields=(news_title;news_text;news_active)>
Thanks in advance guys.
Wayne
You're adding a context variable named "formset" which is not a formset, it's a form: rDict['formset'] = NewsForm().
So, when this context variable is passed to the template, iterating with {% for form in formset %} has the misleading effect of creating a variable named form which is actually a form field. Try naming things properly (if you actually want a formset, create one as described here) and see if things start making sense.
I got a situation.I have a django template ip_form.html given bellow.
<form method = "GET">
{% for val in value_list %}
<input type='text' value = '{{ val }}'> {{ val }}</input>
{% endfor %}
</form>
I want to tell you that, I have unknown no of val in value_list(may be zero). So may not use django form.py( and also I am getting value in certain time of a particular view function, i.e. don't know weather it will occur all time)
Let's say in views.py
def view1(request):
value_list = [1,2,3,4,5] # it will change every time view1 will get request
if request.method == "GET":
# i want to retrieve here post
return render ('ip_form.html','value_list':value_list)
How can i use form.py
So how can I retrieve it. You can refer me post method.(Its not a sensitive data so no problem with get method)
Thanks.
You need to add a name attribute to your inputs, and then you can use this name to retrieve a list of values, using Django QueryDict getlist method:
HTML:
<form method="POST">
{% for val in value_list %}
<input type='text' value='{{ val }}' name='my_list'>{{ val }}</input>
{% endfor %}
</form>
View:
def view1(request):
value_list = [1,2,3,4,5] # it will change every time view1 will get request
if request.method == "POST":
values_from_user = request.POST.getlist('my_list')
return render ('ip_form.html', 'value_list': value_list)
values_from_user will be a list of input values from your form (or an empty list if form had zero input elements).
Please note that I changed your form method to POST, because otherwise the request.method test in your view would be meaningless.