disabling one of the options in WTForms SelectField - python

What is the simplest way to assign 'disabled' html attribute to one of the options in SelectField in WTForms?
This is my previous code
<select class="form-control" name="division" data-error="Required!" required>
<option value="default" disabled selected>Select something</option>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
As you can see I would like to set 'disabled' only to first option.
My code for this task is:
next(form.division.__iter__())(**{'disabled':'true'})
And using print function in console I can see the proper output:
<option disabled="true" selected value="default">Select something</option>
The line is working, but somehow this output is not passed to the template.
Instead this is what's passed:
<option selected value="default">Select something</option>
Please someone make it clear.

If you simply just want a disabled and selected option as the first option of your select, I found this to be simpler than extending the SelectField class:
Just loop over the option and check for first loop iteration
<select name="my_select" id="my_select">
{% for option in form.my_select %}
{% if loop.first %}
<option value="" disabled selected>select something</option>
{% endif %}
{{ option }}
{% endfor %}
</select>

Here's a custom wtforms widget which makes use of the SelectField's Options iterator.
from markupsafe import Markup
from wtforms.widgets.core import html_params
class CustomSelect:
"""
Renders a select field allowing custom attributes for options.
Expects the field to be an iterable object of Option fields.
The render function accepts a dictionary of option ids ("{field_id}-{option_index}")
which contain a dictionary of attributes to be passed to the option.
Example:
form.customselect(option_attr={"customselect-0": {"disabled": ""} })
"""
def __init__(self, multiple=False):
self.multiple = multiple
def __call__(self, field, option_attr=None, **kwargs):
if option_attr is None:
option_attr = {}
kwargs.setdefault("id", field.id)
if self.multiple:
kwargs["multiple"] = True
if "required" not in kwargs and "required" in getattr(field, "flags", []):
kwargs["required"] = True
html = ["<select %s>" % html_params(name=field.name, **kwargs)]
for option in field:
attr = option_attr.get(option.id, {})
html.append(option(**attr))
html.append("</select>")
return Markup("".join(html))
When declaring the field, pass an instance of CustomSelect as the widget parameter.
division = SelectField(
"Division",
choices=[("default", "Select something"), ("option1", "Option 1"), ("option2", "Option 2")],
validators=[InputRequired()],
widget=CustomSelect(),
default="default",
)
And to pass attributes when rendering
form.division(option_attr={"division-0": {"disabled": ""} }, class="form-control")

When you disable the field the values are no longer passed. What you might want to do is pass the variable by using read only instead of disabled. Here's how do to it with jquery :
$('#id_of_option').prop('readonly', true);
Also what I've done is set choices in Wtforms where
choices = [('', 'please select an option'), ('1', 'option 1'), ('2', 'option 2')]
and this way a user has to select a value.

This worked for me.
<select id="{{ form.my_select.id }}" name = "{{ form.my_select.name }}">
{% for option in form.my_select %}
{% if loop.first %}
{{ option(disabled="") }}
{% else %}
{{ option }}
{% endif %}
{% endfor %}
</select>
The HTML 'disabled' attribute is a boolean one and in regular HTML, it does not need to be assigned to "". Without the equal sign, I got some positional argument errors.
If you want to disable other options (say, 4th and 5th option in addition to the first one), you can do this:
<select id="{{ form.my_select.id }}" name = "{{ form.my_select.name }}">
{% for option in form.my_select %}
{% if loop.first %}
{{ option(disabled="") }}
{% elif loop.index == 4 or loop.index == 5 %}
{{ option(disabled="") }}
{% else %}
{{ option }}
{% endif %}
{% endfor %}
</select>
Note that loop.index is 1-based.

Related

Set "selected" tag in responsive dropdown and select using Flask and jinja2

I have a flask server which sends a dictionary of options to a dropdown or selection (in form).
Here is the Python code:
#app.route("/", methods=['GET'])
def index_get():
options = {'o1' : 'option1', 'o2' : 'option2', 'o3' : 'option3'}
return flask.render_template("index.html", options=options)
This is the Jinja code:
<form method="post">
<select name="test">
{% for id,name in options.items() %}
<option selected="" value="{{ id }}">{{name|safe}}</option>
{% endfor %}
</select>
</from>
Now, I want to set the selected attribute to one of the option, for example, option2 from the Python code like return flask.render_template("index.html", options=options, selected='option2') or selected='o2'.
Can someone help me implementing this?
Once you have passed your desired options to the view, via
return flask.render_template("index.html", options=options, selected='o2')
You just have to make a condition that would assert if the selected variable is the same as the current id in the loop, in order to add the selected attribute to the option or not:
<form method="post">
<select name="test">
{% for id,name in options.items() %}
<option {% if selected == id %}selected{% endif %} value="{{ id }}">
{{name|safe}}
</option>
{% endfor %}
</select>
</from>

Django - Passing multichoice field into POST dictionary

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).

Return the string value of what is selected in the drop down list

I have a list of stops:
<select name="dropdown">
{% for stop in stop_list %}
<option value ="{{ stop.name }}">{{ stop.name }}</option>
{% endfor %}
</select>
and a Stops model where one of the attributes is name.
I also have a method find_direction() in my views.py that requires a stop.location input.
How do I record the value of the selected stop.name so I can then get the stop.location to use in views.py?
Thanks!
Currently my method is:
def find_direction(request, stop1name, stop2name):
stop1 = get_object_or_404(Stops, name=stop1name)
stop2 = get_object_or_404(Stops, name=stop2name)
which gives the error: find_direction() missing 2 required positional arguments: 'stop1' and 'stop2name'.. How do I make it record these two variables correctly from the selected drop down list?
Using forms but for some reason..
print(request.GET.get("stop1id", "default_value"))
print(request.GET.get("stop2id", "default_value"))
print(request.method)
print(request.POST.dict())
print(request.GET.dict())
print(request.body)
all return empty
In light of your update, you will most like want to pass the data in a request:
def find_direction(request):
stop1 = get_object_or_404(Stops, name=request.POST.get("stop1name","default_value"))
stop2 = get_object_or_404(Stops, name=request.POST.get("stop2name","default_value"))
This assumes you are sending a POST request from a form back to the server with the stop name data. The dictionary keys stop1name and stop2name should match the names as they are set in your form:
<form action="/get_stop_locations">
<select name="stop1name">
{% for stop in stop_list %}
<option value ="{{ stop.name }}">{{ stop.name }}</option>
{% endfor %}
</select>
<select name="stop2name">
{% for stop in stop_list %}
<option value ="{{ stop.name }}">{{ stop.name }}</option>
{% endfor %}
</select>
</form>
If you are curious, you were receiving an error because positional arguments for django views are set up as captured bits of a url:
urls.py
urlpatterns = patterns('django_app.views',
url(r'^get_stop_locations/(\w+)/(\w+)$','positional_get_locations'),
)
views.py
def positional_get_locations(request,stop1name,stop2name):
stop1 = get_object_or_404(Stops, name=stop1name)
stop2 = get_object_or_404(Stops, name=stop2name)
Django views requires one argument by default which will store the request object. When there are more views defined, the view expects more arguments to be provided by the url handler. If the url is not set up to pull as many arguments as possible in your urls.py file, you will see this error.
For your case, this requires setting up some tricky URLs however on the client side and I wouldn't recommend it. See this example in the DJango Docs for more information on positional arguments in Django views.
EDIT:
In response to your MultiValueDictKeyError, try:
def find_direction(request):
stop1 = get_object_or_404(Stops, name=request.POST.get("stop1name","default_value"))
stop2 = get_object_or_404(Stops, name=request.POST.get("stop2name","default_value"))
If you have your dropdown located in a form like this:
<form method="post" action="">
<select name="dropdown">
{% for stop in stop_list %}
<option value ="{{ stop.name }}">{{ stop.name }}</option>
{% endfor %}
</select>
<input type="submit" />
</form>
You can get the value by defining the following in your views:
def post(self, request, *args, **kwargs):
if 'dropdown' in request.POST:
dropdown_val = request.POST.get('dropdown', False)

Jinja2 to put a whole element in <option>

I need to dynamically select from the following:
So that I can show the selected element depending on POST request.
I want to templatize selected="selected" so that I can choose where to put.
<select name="my_name">
<option value="5min">5-Min</option>
<option value="1hour" selected="selected">Hour</option>
<option value="1day">Day</option>
</select>
Assume target is the desired value that you want to select (obtained from the POST dictionary).
Then you need two things:
Prepare a dictionary containing value - display text pairs for all options in the select, e.g
mydict = {'5min': '5-Min', '1hour': 'Hour', '1day': 'Day'}
In yourtemplate.html:
<select name="my_name">
{% for key, value in mydict.items() %}
<option value="{{key}}"
{% if (key == target) %} selected="selected" { %endif %}
>
{{value}}
</option>
{% endfor %}
</select>
How to pass the target - in your view you need to do this (I assume you know the basics of views in Flask)
target = request.form['key_of_the_data_we_need'] # the value that should be selected
mydict = {'5min': '5-Min', '1hour': 'Hour', '1day': 'Day'} # helper for the select
return render_template('yourtemplate.html', target=target, mydict=mydict)
This way, the data is sent to yourtemplate.html which contains the code discussed above, therefore selecting the desired <select> option.
Using the same approach as Pawel did, I just changed some small things in order to work with my actual version of Jinja2:
<select class="form-control" id="worker" name="worker">
{% for key in workers %}
<option value="{{key}}" {% if key == people.worker %} selected="selected" {% endif %}>
{{key}}
</option>
{% endfor %}
</select>
And from the controller:
workers = {'First One','Second One', 'Third One'}
return render_template('edit.html', people=people, workers=workers)
Remember to put the controller code within the controller which is passing the data for the view.
It is not totally clear to me what the background of your question is. If the reason is that you can't use wtf.quick_form(yourform) because you need some customization, you could simply use
{{ form.workers.__call__(**{'class': 'form-control'}) }}
for the respective field in your Jinja2 template. This selects the posted element in the form. Note how I define additional attributes (a class).
The result will be (for example):
<select class="form-control" id="worker" name="worker">
<option value="0">text1</option>
<option selected value="1">text2</option>
<option value="2">text3</option>
</select>
This of course requires that you have a properly defined form:
class yourform(FlaskForm):
workers = SelectField(
'name',
validators=[Required()],
choices=[(0, 'text1'), (1, 'text2'), (2, 'text3')],
coerce=int
)
(... whatever other fields here ...)

Django custom form template

I've a custom template to render my form in my Django website. This code renders the ChoiceFields:
<select id="{{ "id_"|add:field.html_name }}"
class="form-control"
name="{{ field.html_name }}">
{% for id, name in field.field.choices %}
<option value="{{ id }}"
{% if field.value|default_if_none:"" == id %}
selected="selected"
{% endif %}
>{{ name }}</option>
{% endfor %}
</select>
This code works perfectly, when the ChoiceField does not have an initial value. If I sumbit the form, but there's an error in the form (sp I get back to my form, and all the data is in there as I submitted it), the correct choice get the selected="selected" attribute, so it works perfectly fine.
But when I set the default value for the form in my Django view (correctly in the form's init() function, like this: self.fields['card_type'].initial = self.card.card_type_id), this stops working.
If I put manually somewhere in my template the form.value variable it displays the correct integer. In the id, name for-loop there's the same id value. These do not have any whitespace ot something else there. But the code does not works, does not equals to true the self.fields['card_type'].initial = self.card.card_type_id condition.
If I modify the previous code, to print the id, and the field.value instead of the {{name}}, like this: ({{id}}-{{ field.value }}), the following HTML code'll be generated:
<select id="id_card_type" class="form-control" name="card_type">
<option value="3">(3-2)</option>
<option value="2">(2-2)</option>
<option value="1">(1-2)</option>
</select>
There's the id, which is 1, 2, and 3! There's the form.value, which is 2! But there isn't any selected="selected"... Why not working?
Thanks!

Categories

Resources