Difference between Django Form 'initial' and 'bound data'? - python

Given an example like this:
class MyForm(forms.Form):
name = forms.CharField()
I'm trying to grasp what the difference between the following two snippets is:
"Bound Data" style:
my_form = MyForm({'name': request.user.first_name})
"Initial data" style:
my_form = MyForm(initial={'name': request.user.first_name})
The documentation seems to suggest than "initial is for dynamic initial values", and yet being able to pass "bound data" to the constructor accomplishes exactly the same thing. I've used initial data in the past for dynamic values, but I'm tempted to use the more straightforward "bound data" style, but would like some insights about what the real difference between these two styles is.

Here's the key part from the django docs on bound and unbound forms.
A Form instance is either bound to a set of data, or unbound:
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
You can't really see the difference for the example form you gave, because the form is valid in the "bound data" style. Let's extend the form by adding an age field, then the difference will be more obvious.
class MyForm(forms.Form):
name = forms.CharField()
age = forms.IntegerField()
Bound form
my_form = MyForm({'name': request.user.first_name})
This form is invalid, because age is not specified. When you render the form in the template, you will see validation errors for the age field.
Unbound form with dynamic initial data
my_form = MyForm(initial={'name':request.user.first_name})
This form is unbound. Validation is not triggered, so there will not be any errors displayed when you render the template.

No, that's not what the difference is (and I'd be interested to know where in the documentation you got that impression from). The difference is whether validation is performed.
Initial data does not trigger validation. This allows you, for example, to pre-fill certain fields but leave others empty, even though they are required. If you used bound data, you would get errors for those empty required fields even on the first viewing of that form, which would be annoying for the user.
Bound data, of course, triggers validation. Also, if you're using a modelform, the related instance will only be updated with bound data, not initial data.

Another difference is that data expects something that widgets can parse whereas initial is per-field. This makes a difference if you e.g. use MultiWidgets. In such case data should contain something like
{'myfield_0': 'data for subwidget 0',
'myfield_1': 'data for subwidget 1'}
whereas initial expects something like this:
{'myfield': 'data for subwidget 0,data for subwidget 1'}

Related

Formset Data Initialization

I'm trying to understand how the initialization of formset data works. To create a formset you use formset_factory and define arguments "extra" and "max_num", which determine the number of extra blank forms after the initial/filled forms and the max number of forms posted respectfully. But then, when you create an instance of the defined formset to pass to the template as data, you pass into that instance more data, specifically 'form-TOTAL_FORMS', 'form-INITIAL_FORMS', and 'form-MAX_NUM_FORMS'. But haven't we already defined in the formset_factory the max_num_forms? Why are we defining this twice? For documentation initial forms seems to be just what it sounds--forms already filled in, which is different than "extra". Just don't understand defining the max arg twice.
According to the docs:
You may have noticed the additional data (form-TOTAL_FORMS,
form-INITIAL_FORMS and form-MAX_NUM_FORMS) that was required in the
formset’s data above. This data is required for the ManagementForm.
This form is used by the formset to manage the collection of forms
contained in the formset. If you don’t provide this management data,
an exception will be raised.
https://docs.djangoproject.com/en/2.2/topics/forms/formsets/#understanding-the-managementform

Django form has no errors, but is not valid? [duplicate]

Given an example like this:
class MyForm(forms.Form):
name = forms.CharField()
I'm trying to grasp what the difference between the following two snippets is:
"Bound Data" style:
my_form = MyForm({'name': request.user.first_name})
"Initial data" style:
my_form = MyForm(initial={'name': request.user.first_name})
The documentation seems to suggest than "initial is for dynamic initial values", and yet being able to pass "bound data" to the constructor accomplishes exactly the same thing. I've used initial data in the past for dynamic values, but I'm tempted to use the more straightforward "bound data" style, but would like some insights about what the real difference between these two styles is.
Here's the key part from the django docs on bound and unbound forms.
A Form instance is either bound to a set of data, or unbound:
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
You can't really see the difference for the example form you gave, because the form is valid in the "bound data" style. Let's extend the form by adding an age field, then the difference will be more obvious.
class MyForm(forms.Form):
name = forms.CharField()
age = forms.IntegerField()
Bound form
my_form = MyForm({'name': request.user.first_name})
This form is invalid, because age is not specified. When you render the form in the template, you will see validation errors for the age field.
Unbound form with dynamic initial data
my_form = MyForm(initial={'name':request.user.first_name})
This form is unbound. Validation is not triggered, so there will not be any errors displayed when you render the template.
No, that's not what the difference is (and I'd be interested to know where in the documentation you got that impression from). The difference is whether validation is performed.
Initial data does not trigger validation. This allows you, for example, to pre-fill certain fields but leave others empty, even though they are required. If you used bound data, you would get errors for those empty required fields even on the first viewing of that form, which would be annoying for the user.
Bound data, of course, triggers validation. Also, if you're using a modelform, the related instance will only be updated with bound data, not initial data.
Another difference is that data expects something that widgets can parse whereas initial is per-field. This makes a difference if you e.g. use MultiWidgets. In such case data should contain something like
{'myfield_0': 'data for subwidget 0',
'myfield_1': 'data for subwidget 1'}
whereas initial expects something like this:
{'myfield': 'data for subwidget 0,data for subwidget 1'}

Flask + WTForms, dynamically generated list of fields

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.

How to properly validate a MultipleChoiceField in a Django form

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.

Django custom (multi)widget input validation

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.

Categories