Django - Passing multichoice field into POST dictionary - python

I have a queryset generated in my forms.py file that passes into my template. The template result is a multichoice field based on the queryset. The web browser presentation is correct - it renders the queryset as a drop down choice list that I can make a selection from.
Here is the template code:
<tr><td>{{ form.jury_name | placeholder:'Jury Name' }}</td></tr>
<tr><td><select>
{% for item in form.parent_jury.field.queryset %}
<option name="parent_jury" value="{{ item }}">{{ item }}</option>
{% endfor %}
</select></td></tr>
This is all contained in a table.
When the form is submitted (method = "POST") the POST dictionary has all the correct values for the keys except the parent_jury key which posts a value of ''.
I've worked through several SO solutions on the views.py side, but they don't change the fact that the information available for a clean() is missing the choice field value for 'parent_jury'. How do I get the selected option from the list to attach to the 'parent_jury' key?

I think your rendered HTML is not the way it is supposed to be: the name="..." should be part of the <select> tag, not the <option>s:
<tr><td>{{ form.jury_name | placeholder:'Jury Name' }}</td></tr>
<tr><td><select name="parent_jury">
{% for item in form.parent_jury.field.queryset %}
<!-- remove the name here -->
<option value="{{ item }}">{{ item }}</option>
{% endfor %}
</select></td></tr>
(of course you can remove the <!-- comment --> part (this is only meant to draw attention to this change).

Related

Django - Two forms on one page, how can I maintain URL parameters when either form is submitted?

I'm building an application that contains a list of meals, where each meal has various filters, a price, and a rating.
The filters are like tags; the user can select multiple, and the page only shows the meals that have the selected filters.
The price and ratings are integers, and the user can sort by either price or rating, which sorts the meals (cheapest -> most expensive for price, highest -> lowest for rating).
I have built two forms in Django, one for filters and one for sorting, and they both work on their own. However, let's say I submit the sorting form to sort by price; when I do this, it does sort by price, but it removes all of the prior filters I had submitted.
Below are the important pieces of code relevant to this problem:
views.py
def meals(request):
meal_list = Meal.objects.all()
tags = Tag.objects.all()
reviews = Review.objects.all()
filter_form = FilterForm(request.GET or None)
sorting_form = SortingForm(request.GET or None)
sort = ""
active_filters = []
if filter_form.is_valid():
tags = filter_form.cleaned_data.get('tags')
for tag in tags:
meal_list = meal_list.filter(tags__name=tag)
active_filters.append(tag)
if sorting_form.is_valid():
sort = sorting_form.cleaned_data.get('sort')
if sort == "price":
meal_list = meal_list.order_by('price')
else:
meal_list = meal_list.order_by('-rating')
paginator = Paginator(meal_list, 8)
page_number = request.GET.get('page')
meals_on_page = paginator.get_page(page_number)
context = {"meal_list": meal_list,
"distances": distances,
"tags": tags,
"reviews": reviews,
"active_filters": active_filters,
"meals_on_page": meals_on_page,
"filter_form": filter_form,
"sorting_form": sorting_form,
}
return render(request, 'meals/meals.html', context)
forms.py
from django import forms
# Tag is the model for the filters, it is just a ManyToManyField that contains a name attribute
from .models import Tag
class FilterForm(forms.Form):
tags = forms.ModelMultipleChoiceField(
queryset=Tag.objects.all(), widget=forms.CheckboxSelectMultiple)
class SortingForm(forms.Form):
SORT_CHOICES = [
('price', 'Price'),
('rating', 'Rating'),
]
sort = forms.ChoiceField(choices=SORT_CHOICES, widget=forms.Select)
meals.html
<form method="get">
{% for field in filter_form %}
{{ field.as_widget }} {{ field.label_tag }}
{% endfor %}
<input type="submit" value="Filter">
</form>
<form method="get">
{% for field in sorting_form %}
{{ field.as_widget }}
{% endfor %}
<input type="submit" value="Sort">
</form>
I have sadly way too long trying to fix this, and the closest I got was using get_copy = request.GET.copy() and then trying to manually add the URL parameters back onto the end of a URL after a form was submitted. However, none of my approaches using this seemed to work.
Thanks in advance for the help!
In your Django view, you can access the current URL parameters using the request object's GET attribute. To maintain these parameters when either form is submitted, you can include them in the form action attribute in your template.
For example, in your template, you can update the form action attribute to include the current URL parameters like this:
<form method="get" action="{% url 'meals' %}?{{ request.GET.urlencode }}">
This will append the current URL parameters to the form action, so when the form is submitted, the current parameters will be included in the request.
In your view, you can then access these parameters using the request.GET dictionary. You can use these parameters to filter and sort your queryset accordingly before rendering the template.
Note: you should also check if the forms are valid before processing the form data to avoid unexpected behavior.
Also, you can use Django's forms.HiddenInput() to include the current parameters on your forms as hidden fields, that way you don't need to update form's action attribute.
I ended up solving this after soaking (way too many) more hours into it.
The way I did this was to build my own dictionary of parameters, and then to pass these parameters into the forms as hidden inputs.
Below is the added code:
views.py
get_copy = request.GET.copy()
parameters = get_copy.urlencode()
get_copy.pop('page', None)
param_list = parameters.split("&")
param_dict = defaultdict(list)
for param in param_list:
try:
key, value = param.split("=")
except ValueError:
# Handle the case where there's no "=" in the parameter string
key = param
value = ""
if key == "tags":
param_dict[key].append(int(value))
else:
param_dict[key].append(value)
# Django requires me to turn dictionary to items here, rather than in the template
param_dict_items = param_dict.items()
meals.html
<form method="get" action="{% url 'meals' %}?{{ request.GET.urlencode }}">
{% for field in filter_form %}
{{ field.as_widget }} {{ field.label_tag }}
{% endfor %}
{% for key, value_list in param_dict_items %}
{% if key != 'tags' %}
{% for value in value_list %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
{% endif %}
{% endfor %}
<input type="submit" value="Filter">
<form method="get" action="{% url 'meals' %}">
{% for field in sorting_form %}
{{ field.as_widget }}
{% endfor %}
{% for key, value_list in param_dict_items %}
{% if key != 'sort' %}
{% for value in value_list %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
{% endif %}
{% endfor %}
<input type="submit" value="Sort">

Save the passed value from python in html select tag form

I am passing a string value to select tag in html form and its working properly. But when I load the page again or run the script again, the previously passed values are not shown in drop down list.
In html form, I am passing value like this:
<option value="{{x}}">{{x}}</option>
In python:
x = "Example"
return render_template('example.html', x=x)
can anyone please let me know how to save this value so that it is available for selection in drop down list.
Thanks in advance!
You could set a list, append new option values in it and then pass the list to your html template. Something like this;
# app.py
optionsList = []
optionsList.append('Example')
optionsList.append('Example2')
return render_template('example.html', options=optionsList)
# example.html
{% if options %}
<select name="foo" id="foo">
{% for option in options %}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>
{% endif %}

Is there a way to dynamically set the default value of a wtform radiofield from within the html template?

I'm trying to dynamically set the value of a radio field within the HTML template but I'm not sure how to set the default selection. I want to do this because the forms I want to use are forms that can be saved and re-edited if need be, so I want the default values to be values set when previously saved/submitted.
the wtform fields
field_1 = StringField('field_1')
radio_1 = RadioField('radio_1', choices=[(1,'Yes'),(2,'No')])
what i want to be able to do is something like this:
data is database data
{% if data.field_1 = 'X' %}
{{ form.radio_1(id="radio_1",class="ff-style-radio",default=1) }}
{% else %}
{{ form.radio_1(id="radio_1",class="ff-style-radio",default=2) }}
{% endif %}
I haven't had any success trying this method, swapping default for value, etc. Is something like this possible? If not how would I separate the wtform radio field choices so I can just manually mark which selection is checked? Or should I just use the base HTML method and do something like this:
<ul class="ff-style-radio" id="radio_1">
<li>
{% if data.field_1 = 'X' %}
<input id="radio_1-0" name="radio_1" type="radio" value="Yes" checked>
{% else %}
<input id="radio_1-0" name="radio_1" type="radio" value="Yes">
{% endif %}
<label for="radio_1-0">Yes</label>
</li>
<li>
{% if data.field_1 = 'X' %}
<input id="radio_1-1" name="radio_1" type="radio" value="No">
{% else %}
<input id="radio_1-1" name="radio_1" type="radio" value="No" checked>
{% endif %}
<label for="radio_1-1">No</label>
</li>
</ul>
Thanks for any help
If you want to use the form to edit data already in your database, you should supply this data to the form at creation time in your view function. This will allow you to keep your templates simple and reusable. Data from the database can be passed to the form either as an object or as a dictionary https://wtforms.readthedocs.io/en/stable/forms.html. If your form maps directly to a database table you can just pass this unmodified from your query. In the following code I've followed your example and created a new dictionary to set the value of radio_1 based on the value of field_1.
#app.route("/myurl")
def myview():
# do database lookup here
olddata = {"radio_1": 1 if data.field_1 == "X" else 2}
form = Myform(formdata=request.form, data=olddata)
if request.method == "POST" and form.validate():
# .....

Passing a Tuple of Values through a Checkbox

I have a list of organized items that users may choose from using checkboxes. I am trying to pass a tuple of values for each checked checkbox so I can get information about the item itself and the group to which the item belongs. I have tried using hidden fields but it seems that the hidden field value is being passed regardless of if the corresponding checkbox has been checked or not.
If a checkbox is checked, I need the citation id and the parent software. Can I pass a tuple of (citation.id, sw) for each checked checkbox and, because multiple checkboxes can be checked, pass all of these together as a list of tuple? Like: [(citation1.id, sw1), (citation2.id, sw2), ] ? I need this info in my view.
Thank you for any help!
select_citations.html
{% for sw in software %}
{{sw}}
{% for citation in all_citations %}
<input type="checkbox" name="myselection[]" value="{{citation.id}}">
<input type="hidden" name="myselection[]" value="{{sw}}">
{% endfor %}
{% endfor %}
Compose IDs of both models to a single value for the checkbox:
{% for sw in software %}
{{sw}}
{% for citation in all_citations %}
<input type="checkbox" name="selection" value="{{citation.id}}-{{sw.id}}">
{% endfor %}
{% endfor %}
And then deconstruct this values in the view:
ids = [value.split('-') for value in request.POST.getlist('selection')]

Optimize a drop down for django 1.3

Trying to optimize a django application which is using 1.3. Migrating to latest django is not yet an option, as it's a huge application.
So there's this code in the template:
<select id="item_product">
{% for ip in items %}
<option value="{{ ip.program.id }}/{{ ip.sector.id }}/">{{ ip }}</option>
{% endfor %}
</select>
This seems to generate DB calls for every option - making it pretty slow to just load a page with just a drop-down and a button! Replacing the option values with dummy strings indeed immediately loads the page.
The view is very simple:
#render_to('pick_item.html')
def pick_item(request):
person = request.user.get_profile()
items = ItemProduct.objects.filter(program__in=person.programs)
return {'items': items }
And this code returns pretty fast.
How can I optimize this code for django 1.3 so that the drop-down options have the ids I need more efficiently?
If you only need ID's:
<select id="item_product">
{% for ip in items %}
<option value="{{ ip.program_id }}/{{ ip.sector_id }}/">{{ ip }}</option>
{% endfor %}
</select>
If you need something more complex:
#render_to('pick_item.html')
def pick_item(request):
person = request.user.get_profile()
items = ItemProduct.objects.filter(program__in=person.programs).select_related('program', 'sector')
return {'items': items }

Categories

Resources