Dynamic Forms (Formsets) in Flask / WTForms? - flask

In Django you have a multiple form feature called Formsets, which you can use to create multiple forms into the same template. I am trying to achieve something similar in Flask / WTforms.
<form action="{{ url_for('request-accept') }}" method='post'>
<table>
<tbody>
{% for request in requests %}
<tr>
<td>
<div class="person-header">
<img src="{{request.profile_pic_url}}" class="img-circle profile-image"/>
<p class="person-header-text">{{request.fullname()}}</p>
</div>
</td>
<td>
<input type="checkbox" id="{{request.key.urlsafe()}}" name="checkbox{{loop.index}}">
</td>
</tr>
{% endfor %}
</tbody>
</table>
<input class='submit btn btn-primary' type=submit value="Connect">
</form>
The idea is having one form that wrapps all the checkboxes, which the user like to tick to become friends with. As it currently stands I am not really generating any form class in Flask, since I don't know how to make a dynamic FormSet, hence I create the form dynamically inside the html.
The caveat is though, I don't know how to retrieve the selected user id via the checkbox. (I have stored it in the id because I didn't know better)
But I can't access the id in request.values['checkbox1']. I can only see if its on or off.
Any suggestions how to solve this please?

The problem
Your problem is that id is not sent back to the server - only value is ... and since your checkboxes don't have a value attribute the default value is used, which happens to be on.
Since you tagged this with wtforms, I'll give you an example of how you could be doing this.
Never have this issue again
The WTForms' documentation has an example class that will create a list of checkboxes for you:
class MultiCheckboxField(SelectMultipleField):
"""
A multiple-select, except displays a list of checkboxes.
Iterating the field will produce subfields, allowing custom rendering of
the enclosed checkbox fields.
"""
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
You would use this field in your custom form in this manner:
class FriendsForm(Form):
potential_friends = MultiCheckboxField("Friends", coerce=int)
# ... later ...
#app.route("/add-friends", methods=["GET", "POST"])
def add_friends():
form = FriendsForm(request.form)
# lookup friends, for now we'll use a static list
form.potential_friends.choices = [(1, "Sam"), (2, "Joe")]
# We'll also need a mapping of IDs to Person instances
# (Made up for this example - use your own ;-) )
mapping = {
1: Person("Sam", profile_pic="sam.jpg"),
2: Person("Joe", profile_pic="joe.png")
}
if request.method == "POST":
# Mark new friends
return render_template("friends.html", form=form, persons=mapping)
Then, in friends.html you can iterate over the form.potential_friends field:
{% for person in form.potential_friends %}
persons[person.data].profile_pic :: {{person.label}} :: {{person}}<br>
{% endfor %}
You can customize your HTML inside the for loop. My particular example should render (with a few more attributes, like for and name):
sam.jpg :: <label>Sam</label> :: <input type="checkbox" value="1">
joe.png :: <label>Joe</label> :: <input type="checkbox" value="2">

I personnally would add a hidden input field in your fieldset under each checkbox with a name such as "friend_nametag1" and a value corresponding to the friend's ID. With the 1 being incremented for every "friend". You can thus look it up in flask view using something like
friend_list = []
list_of_checkboxes = ... (fetch from request.form... ?)
dict_of_friend_nametags = ... (build from request.form... ?)
if 'checkbox1' in list_of_checkboxes:
friend_list.append(dict_of_friend_nametags.get('friend_nametag1')
Obviously you can use some sort of logic to have an incremental index (the "1" in "checkbox1" in this case).
I'm not too familiar with WTForms so there might be a better way to do this, but this solution is fairly straightforward to implement with your current code.
If you want a FieldSet or FormSet I would suggest you use the FormField in conjunction with the FieldList with the related docs here : http://wtforms.simplecodes.com/docs/dev/fields.html#field-enclosures
p.s.: I would not recommend using request as a variable name in your template or your code as it may shadow the global request of flask ? XD

Related

dynamic form in Flask

in a movie rating app,I would like to generate a WTF form in flask with dynamic number of fields. i.e, if there are three movies, there will be three fields.
I thought about a few options, but none of them worked:
class RatingForm(Form):
rating = TextField("rating",[validators.Length(min=1, max=1)])
movie_order=TextField("movie",[validators.Length(min=1, max=1)])
submit = SubmitField("submit rating")
pass a parameter to the form object - I don't see how can I pass a parameter to this kind of class
make a loop inside the template, thus generate and return multiple forms, and choose the correct one. this also doesnt work, since the request.form is immutableDict, and I end up having multiple fields with the same key, which I cant access.
{% for movie in movies_to_rate %}
<p>
<form method="POST" enctype="multipart/form-data" action="/rate">
{{ movie}}
{{ forms[movie].rating}}
{{ forms[movie].submit }}
<input type="submit" value="Go">
</p> {% endfor %}
any ideas about what can I do?
I think you can generate a list of TextField's as a class member instead of using one field object. (Though it looks a bit weird, I assume your validators are what you meant.)
class RatingForm(Form):
def __init__(self, count):
self.ratings = [TextField("rating_" + str(i), [validators.Length(min=1, max=1)])
for i in range(count)]
...

Django URL/Views extra parameters

In Django1.6, is there a way to pass a dynamic parameter to my view or URL without parsing the URL?
Ideally I would want a urls.py that looks like:
url(r'^dash/$',
dash_view.account_modify,
{'account': **dynamic_account_identifier_here**}
name='dash_account_modiy')
And in views.py:
def account_modify(request, account,
template_name='profile.html,
change_form=AccountModifyForm):
...
:param account:
comes from model:
class Dash(models.Model):
name = models.Charfield()
account = models.IntegerField()
....
Basically, I would really like to avoid a urls.py with the account identifier as part of the string, like:
url(r'^dash/(?P<account>\w+)/$',
dash_view.account_modify,
name='dash_account_modiy')
Any suggestions on how I can pass these values from the a template to the processing view for use in the AccountModifyForm (which expects that 'account' parameter)?
url(r'^dash/$',
dash_view.account_modify,
{'account': **dynamic_account_identifier_here**}
name='dash_account_modify')
You can't dynamically evaluate anything there, because the dictionary is evaluated only once, when the URL conf is loaded.
If you want to pass information from one view to another your three options are:
in the URL, which you don't seem to want to do
as GET or POST data
store it in the session in one view and retrieve it from the session in the next
If anyone cares...figured it out...
In the template:
{% for dash in dashes %}
blah blah blah
<form action="..." method="POST">
<input type="hidden" name="id" value="{{ dash.account }}">
{{ form.as_ul }}
<input type="submit" value="Do stuff">
</form>
{% endfor %}
In the views:
if request.method == 'POST'
account = request.POST['id']
# be sure to include checks for the validity of the POST information
# e.g. confirm that the account does indeed belong to whats-his-face
form = AccountModifyForm(request.POST, account,
user=request.user)
....

Muliple instances of modelform

I have a list in my template. For each item in the list, I have a {{ modelform }} that contains a checkbox. I can check the box and it updates as should. The problem is that when I check the box for one item and submit, it submits for all of the checkboxes because they are the same in each instance. Is there a way to set up a unique checkbox instance for each item in the list?
Current each modelform checkbox renders the same like this:
<input name="is_solution" type="checkbox" class="is_solution" id="is_solution">
I also tried using
test = request.POST.get('checkbox')
and
test = request.POST.get('checkbox')
thinking that using this I might be able to post an update in my view. I think I am going about this all wrong and I am lost. Essentially, I would like to have a checkbox on a list much like here on stackexchange where you can confirm an answer. Any suggestions?
You have to use form's prefix in the view like (just something unique for each form object):
def foo(request, ...):
objs = Model.objects.filter(...)
forms = []
for i, obj in enumerate(objs):
form = ModelForm(instance=obj, prefix=str(i))
forms.append(form)
...
This will make sure each form has unique identifier, hence you will be able to submit a specific form.
And you can render the forms like usual in the template:
<form ...>
{% csrf_token %}
{% for form in forms %}
{{ form }}
{% endfor %}
</form>

submit form to django-admin model with filter

I would like to have a custom snippet of html form code that takes allows the user to select a 'training' that is then used as a query parameter to a django-admin model filter for 'participants'.
I've successfully created the filter on the modeladmin:
class ParticipantAdmin(RestrictedModelAdmin):
list_filter = ('training__name',)
It's probably worth noting that RestrictedModleAdmin is a subclass of ModelAdmin that provides row-level security for the model; logged in users should only see rows they own.
Thus, urls using this filter look something like this when just using that admin interface:
/admin/core/participant/?training__name=Menno+Ropes
All that works great. Now I think I should be able to create a very simple form that allows selecting a valid 'training' and submitting that to /admin/core/participant/ as a GET.
<form method="GET" action="/admin/core/participant/">{% csrf_token %}
<ol>
<li>Select your training:
<select name='training__name'>
<option value=''>—</option>
{% for training in trainings %}
<option value='{{ training.name }}'>{{ training }}</option>
{% endfor %}
</select>
</li>
<li>See participants for that training.
<input type='submit' name='submit' value='Submit' /></li>
</ol>
</form>
This last bit doesn't see to work. Some magic foo in the django innards seems to always mangle the submission to be:
/admin/core/participant/?e=1
This obviously doesn't select the appropriate filter value and thus shows an unfiltered list of 'participants'.
What's going on? What can I do to get it to allow my GET parameter to pass through to the admin model?
Thanks in advance.
PS) Django 1.3+
The problem is that you have a name attribute in your <input type="submit">, causing an extra GET parameter: submit which is throwing the invalid lookup error and thus e=1
Remove the name attribute and you're good to go.
I did a little experiment to confirm since I thought it odd that the server might somehow treat a browser GET differently.
It's a little bit tricky, but it works for me:
def changelist_view(self, request, bill_id, extra_context=None):
"""queryset is an extra parameter"""
req = request.GET.copy()
if 'queryset' in req:
queryset = req.pop('queryset')[0]
else:
queryset = request.META['HTTP_REFERER'].split('queryset=')[1]
url = "/admin/billing/invoice/%s/select_to_move/?%s&queryset=%s" % (bill_id, request.GET.urlencode(), queryset)
return HttpResponseRedirect(url)
request.GET = req
# Do stuff with queryset.
return super(MyAdminClass, self).changelist_view(request, context)

How do I get the value of a BoundField in a django template?

The built in as_html, as_ul, as_p methods on Django forms don't work for me, nor does the built in {{field}} rendering, so I'm trying to write a custom form rendering.
Here's what I have so far:
<input id="id_{{field.html_name}}"
type="text"
name="{{field.html_name}}"
placeholder="{{field.label}}" <!-- "placeholder" is really the only reason I need to do a custom implementation -->
value="{{ XXX }}" <!-- what goes here? -->
maxlength="30" />
The question is, what should go in the value attribute (marked XXX above)?
I've done some looking around and it doesn't appear that BoundField supports a value or data attribute. I'm using ModelForms if it matters
Assuming the field name is "username" and the form name is "user_form", there are two values:
1) Initial:
{{ user_form.initial.username }}
2) Bound:
{{ user_form.username.data }}
The value attribute landed in trunk in 2010. The patch shows how to retrieve the value using the form/data (not simple in a template unfortunately). There are some template tag code snippets in the ticket comments you may find useful.
I tried to find an answer for this question for several hours. The above answer didn't help me.
I found the solution here:
http://djangosnippets.org/snippets/2264/
You need to add new directory: /yourproject/yourapp/templatetags/
In /yourproject/yourapp/templatetags/ place 2 files:
__init__.py - empty file
field_value.py - with the following code:
from django import template
register = template.Library()
#register.simple_tag
def field_value(field):
""" returns field value """
return field.form.initial.get(field.name, '')
At the beginning of your template you nedd to add:
{% load field_value %}
Where you want to output a value of a field you need to add:
{% field_value form.field %}
Or if you already have a "field" variable, then just:
{% field_value field %}
For me it was a field with name "text" of an inline form, so I added the following code
{% field_value inline_admin_form.form.text %}