I have a MultipleChoiceField representing US states, and passing a GET request to my form like ?state=AL%2CAK results in the error:
Select a valid choice. AL,AK is not one of the available choices.
However, these values are definitely listed in the fields choices, as they're rendered in the form field correctly.
I've tried specifying a custom clean_state() method in my form, to convert the value to a list, but that has no effect. Printing the cleaned_data['state'] seems to show it's not even being called with the data from request.GET.
What's causing this error?
from django import forms
class MyForm(forms.Form):
state = forms.MultipleChoiceField(
required=False,
choices=[('AL','Alabama'),('AK','Alaska')],
)
MultipleChoiceFields don't pass all of the selected values in a list, they pass several different values for the same key instead.
In other words, if you select 'AL' and 'AK' your querystring should be ?state=AL&state=AK instead of ?state=AL%2CAK.
Without seeing your custom clean_state() method I can't tell you what's going wrong with it, but if the state field isn't valid because the querystring is wrong then 'state' won't be in cleaned_data (because cleaned_data only holds valid data).
Hopefully that helps. If you're still stuck try adding a few more details and I can try to be more specific.
Related
I have a form with a few integer fields dynamically added in a view, those fields are for the user to rank from 1-N. I've had trouble figuring out how to write a validator that can ensure reach field.data has a unique value and are from from 1 to N.
I've figured out how to dynamically add fields to a form per wtforms' docs, but I'd like to validate them all against each other like this question and I haven't figured out how to properly reference the dynamic fields in the overridden validate function.
How can I reference the dynamic fields in my form instance in my validator? In the question linked above they do it in the line:
for field in [self.select1, self.select2, self.select3]:
But since I'm adding those fields dynamically with setattr I don't know those field names. I tried adding a list variable to the Form and appending to that list when I add the dynamic fields but they show up as:
<UnboundField(IntegerField, ('first',), {'validators': [<wtforms.validators.DataRequired object at 0x7ff75a6d7390>]})>
Instead of just IntegerFields if I reference a field like select1 in the example above:
<wtforms.fields.core.IntegerField object at 0x7fac1bd54910>
How can I reference and validate together these integer fields that I add to my form dynamically?
Turns out digging a little closer in the WTForms documentation, I should have been using a FieldList. And even better than that I can use a FieldList to enclose a list of FieldForms, this provides some more flexibility for adding fields dynamically.
I have some form views similar to account.voucher.receipt.dialog.form which is in the file:
/addons_path/account_voucher/voucher_payment_receipt_view.xml.
Some field tags get their default values which were defined in the Model,
Some field tags get their default values from on change methods (defined by on_change attributes).
I want to bypass these form views and automate the process, so I need to know in advance these default field values.
In that way, I only need to add additional field values if needed, then call the create method on the model.
I'm using Odoo v8.
How can I achieve that?
If you want to print in the log all the default values of your model you can do this:
from inspect import isfunction
#api.multi
def get_default_fields(self):
for key, value in self._fields.iteritems():
if value.name not in models.MAGIC_COLUMNS:
if self._defaults.get(value.name, False):
if isfunction(self._defaults[value.name]):
_logger.debug(self._defaults[value.name](
self, self.env.cr, self.env.uid, None
))
else:
_logger.debug(self._defaults[value.name])
I think you can adapt this code to your needs.
And if you want get the value of one field assigned by an onchange method maybe you have to run the method manually
I am making a Flask application that is essentially form-based and so I'm using WTForms and Flask-wtf.
I am currently refactoring my code so my whole form uses WTForms and there is a very dynamic part of one of the forms that I am unable to implement using WTForms. I have no clue how to do it, my initial ideas didn't work, I can't find references or tutorials covering my problem and so this is why I ask for help.
So, the form in question allows users to submit objects that consist of:
A label (StringField, easy)
A Description (TextAreaField, also easy; although I had trouble to make a default value work)
A list of property of the form (predicate, object), where predicate is taken from a pre-built list and object can basically be anything but each predicate will generate a specific object (for instance, the predicate "related to" will expect another object (that comes from a dropdown list) and the predicate "resource" will expect a http link of some sort). This list can be empty.
As you can guess I have trouble with the list. The way the code works right now, I get the label and description using wtforms, and the property list is generated using a config constant (that is used throughout the code so I only have one place to edit if I want to add new properties) and a dynamic menu in javascript that creates (here, for predicates) fields, that I can then get using flask.request.form object in the view function. All the hidden fields for predicates have the same name attribute and all the hidden fields for objects have the same name attribute.
Here is what the view of the form looks like, initialized with a few properties:
http://i.imgur.com/bfMG95s.png
Under the "Propriétés" label you have a dropdown to select the predicate, the second field is displayed or hidden depending on the selected predicate (can be a dropdown or a text field), and it is only when you click on "Ajouter propriété" ("Add property") that a new line is added in the tab below and the fields are generated.
I'd like not to have to change anything on this side because it works very well, makes the form very intuitive and is basically exactly what I want it to be from the user's end.
This is what my custom Form looks like right now (it doesn't work and properties stays empty whatever the number of fields I submit with the form):
class PropertyForm(Form):
property_predicate = HiddenField(
validators=[AnyOf(values=app.config["PROPERTY_LIST"].keys())]
)
property_object = HiddenField(
validators=[DataRequired()]
)
class CategoryForm(Form):
"""
Custom form class for creating a category with the absolute minimal
attributes (label and description)
"""
label = StringField(
"Nom de la categorie (obligatoire)",
validators=[DataRequired()]
)
description = TextAreaField(
"Description de la categorie (obligatoire)",
validators=[DataRequired()]
)
properties = FieldList(FormField(PropertyForm),validators=[Optional()])
And here is what I'd love to do in my views.py code (that I am currently refactoring):
def cat_editor():
cat_form = CategoryForm()
if request.method == "GET":
# Do GET stuff and display the form
return render_template("cateditor.html", form=cat_form, varlist=template_var_list)
else if request.method == "POST":
if cat_form.validate_on_submit():
# Get values from form
category_label = cat_form.label.data
category_description = cat_form.description.data
category_properties = cat_form.properties.data
# Do POST stuff and compute things
return redirect(url_for("index"))
else:
# form didn't validate so we return the form so the template can display the errors
return render_template("cateditor.html", form=cat_form,
template_var_list = template_var_list)
The basic structure works perfectly, it's just that damn dynamic list I can't get to work properly.
Getting label and description from the WTForms CategoryForm instance works fine, but properties always return an empty list. Ideally I'd love to be able to get a list of the form [(predicate1, property1), (predicate2, object2) ... ] when calling cat_form.properties.data (this is why I have a FieldList of FormFields with two HiddenField in each) but I'd have no problem having to build such a list from two list as long as it's using WTForms. Any idea? Thanks a lot :)
I found out what the problem was by playing around with FieldList objects and append_entry() to see what HTML code would Flask-wtf generate if I was to make a prepopulated property list.
My Javascript was generating hidden fields with all the same name, as from what I understood that WTForms is able to aggregate fields with the same name to create lists. Problem is, those similarly named fields were part of a FormField itself nested in a FieldList object name properties.
In order for the WTForms Form object to discern a set of hidden fields from another, when you nest FormFields inside a FieldList it prefixes the FormFields field names with "FieldList_name-index-". Which means what WTForms was expecting was something like
<input type="hidden", name="properties-0-property_predicate" value=...>
<input type="hidden", name="properties-0-property_object" value=...>
<input type="hidden", name="properties-1-property_predicate" value=...>
<input type="hidden", name="properties-1-property_object" value=...>
<input type="hidden", name="properties-2-property_predicate" value=...>
<input type="hidden", name="properties-2-property_object" value=...>
I modified my javascript so it generates the appropriate names. Now when I call cat_form.properties.data I have something that looks like:
[{"property_predicate": "comment", "property_object":"bleh"},
{"property_predicate": "comment", "property_object": "bleh2"}]
And that is exactly what I need. For some reason the form doesn't validate but at least I know how to make WTForms extract data my javascript-generated hidden fields, which is what the problem was.
Edit: Form validation happens because you have to insert a CSRF hidden input with your csrf to every subform you generate with the FormField.
Use jQuery for the more dynamic elements/ behavior in your form(s). Note that form fields have a hidden property (or method, depending e.g., if you're using bootstrap), allowing you to render everything you might need, but only show fields when these are necessary, and hiding them otherwise. Dynamically adding fields is a bit harder, but not really impossible. Is there a limit to the number of fields associated with properties? if yes, i'd just render the maximum number of fields (as long as it's reasonable, up to 5 seems OK, when you get to double digits as a maximum number of properties a user can add, rendering a bunch of fields you'll never use gets to be inelegant).
Here's a good place to see how that would work. Of course, you have another problem of choosing when to hide or show relevant fields, but that can also be handled by a javascript/jQuery script, using jQuery's .change() event. Something like this:
$("#dropdown").change(function () {
var chosen_val = $(this).val();
if (chosen_val == 'banana'){$('#property1').show();} else {$('#property1').hide();}
});
This code will probably not work, and is definitely lacking proper logic but should give you an idea of how to approach this issue using jQuery. Note that 'property1' field is always there, waiting to be shown if the user chooses the right dropdown value.
I have a very complicated form and I choose to not use ModelForm since I needed flexibility and control over the fields. Since I am not using ModelForm, I can't simply do something like instance=order, where order = Order.objects.get(pk=1).
Currently I am pre-populating every field with initial in the forms.py as oppose to the views.py like this
self.fields['work_type'] = forms.ChoiceField(choices=Order.WORK_TYPE_CHOICES, initial=order.work_type)
But I was wondering if I could pass the entire order object to a form or do I have to declare initial to every field?
Is there a way to do something like
order_form = OrderEditForm(data=request.POST, initial=order)
in views.py?
I have a very complicated form and I choose to not use ModelForm since
I needed flexibility and control over the fields
Everything that you can do using a Form, you can do in a ModelForm such as adding new fields or over-riding attributes on the fields etc.
But I was wondering if I could pass the entire order object to a form
or do I have to declare initial to every field?
You can pass the order object into the form but you will still have to populate each field individually either in the forms or in the view function.
So in your view you would do something like this:
intial = {'order_number': order.number, 'order_id': order.id}
form = OrderForm(initial=initial)
The easiest way to prepopulate data to a form is passing a dictionary as first argument to de form constructor.
order_form = OrderEditForm(order.__dict__())
where __dict__() is a method that passes "order" object attributes to a dictionary with each attribute's name as a key and their content as value.
An example of how to "invent" such a method could be something like:
order_initial = Order.objects.filter(pk=order.pk).values()[0]
and then construct the form with:
order_form = OrderEditForm(order_initial)
Look at this example (how they populate values at "post" time):
For future reference to other people:
I have since found out after reading SO's comments and answers that it's better to use ModelForm even if you end up explicitly defining every field manually (using something like self.fields['foo'] = forms.CharField()).
In any case, if you are trying to pass a dictionary of current values in a form then the best (built-in) way to convert a model to a dictionary is actually using model_to_dict:
from django.forms.models import model_to_dict
order = Order.objects.get(pk=1)
dictionary = model_to_dict(order)
form = OrderEditForm(dictionary)
I got the solution from this blog. I hope this will be helpful for someone.
What is the correct method for validating input for a custom multiwidget in each of these cases:
if I want to implement a custom Field?
if I want to use an existing database field type (say DateField)?
The motivation for this comes from the following two questions:
How do I use django's multi-widget?
Django subclassing multiwidget
I am specifically interested in the fact that I feel I have cheated. I have used value_from_datadict() like so:
def value_from_datadict(self, data, files, name):
datelist = [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
try:
D = date(day=int(datelist[0]), month=int(datelist[1]), year=int(datelist[2]))
return str(D)
except ValueError:
return None
Which looks through the POST dictionary and constructs a value for my widget (see linked questions). However, at the same time I've tacked on some validation; namely if the creation of D as a date object fails, I'm returning None which will fail in the is_valid() check.
My third question therefore is should I be doing this some other way? For this case, I do not want a custom field.
Thanks.
You validate your form fields just like any other fields, implementing the clean_fieldname method in your form. If your validation logic spreads across many form fields (which is nto the same as many widgets!) you put it in your form's clean() method.
http://docs.djangoproject.com/en/1.2/ref/forms/validation/
According to the documentation, validation is the responsibility of the field behind the widget, not the widget itself. Widgets should do nothing but present the input for the user and pass input data back to the field.
So, if you want to validate data that's been submitted, you should write a validator.
This is especially important with MultiWidgets, as you can have more than one aspect of the data error out. Each aspect needs to be returned to the user for consideration, and the built in way to do that is to write validators and place them in the validators attribute of the field.
Contrary to the documentation, you don't have to do this per form. You can, instead, extend one of the built in forms and add an entry to default_validators.
One more note: If you're going to implement a MultiWidget, your form is going to pass some sort of 'compressed' data back to it to render. The docs say:
This method takes a single “compressed” value from the field and returns a list of “decompressed” values. The input value can be assumed valid, but not necessarily non-empty.
-Widgets
Just make sure you're handling that output correctly and you'll be fine.