Django ModelChoiceField with empty_label as valid input - python

I'm using the following form to generate a dropdown list with my registered users:
class TaskAssignUserForm(forms.Form):
user = forms.ModelChoiceField(queryset=User.objects.all().order_by('username'))
Inside my template I render the above form together with a submit button. The application user can choose from the registered users to select one and assign him/her to a task. This is working, if a user and not the empty label (--------) was selected.
But, I want the empty label as a valid option too, to cancel the assignment between the task and the user. I'm working with the following views.py and looking for an option to check if the empty label or an empty choice was made.
if form_TaskAssignUserForm.is_valid():
task.user_assigned = form_TaskAssignUserForm.cleaned_data['user']
task.save()
else:
if # check if emtpy label is set
task.user_assigned = None
task.save()
I found out that checking if form_TaskAssignUserForm.cleaned_data['user'] exists could be an option but I feel not comfortable with that. There should be a way that works together with the .is_valid() check.
Is there a djangonian way solving that problem?
Bye, aronadaal

You should just need to set required=False on the field.
user = forms.ModelChoiceField(required=False, queryset=User.objects.all().order_by('username'))
This means that no selection is required for the form to be valid.

Related

How do I redirect PloneFormGen to a page depending on the value in a select variable?

I am using PloneFormGen 1.7.12 in Plone 4.3.3. I have a large selection field that contains about 20 names. It is required. If the user picks one of the names, it should send them to a Plone Page template that has instructions for them. If it picks any other page, it should send the user an email with their form results and takes them to a thank you page.
I have tried doing this with a python script, but what I am doing doesn't work. The code in the script is:
REDIRECT = context.REQUEST.RESPONSE.redirect
URL=context.REQUEST.URL1 + '/'
FORM=context.REQUEST.form
school_name = context.REQUEST.form['school_name']
member = context.portal_membership.getAuthenticatedMember()
roles=member.getRolesInContext(context)
if school_name <> "Other-School not Listed":
return False
else:
return REDIRECT(URL + 'not_listed')
I put python:here.school_redirect() in the Custom Form Action override, but the URL displays:
python:here.school_redirect()
and the page displays "The address wasn't understood"
school_redirect is the id of the python script
not_listed is the id of the Plone page template
school_name is the id of the select field
Any suggestions are greatly appreciated.
Joe Bigler
The Custom Form Action is a simple StringField, no a TALES expression field.
This means you can add ./school_redirect to the Custom Form Action, but then you got other issues like returning False will not work, you need to redirect anyway. You also bypasses all validators and success handlers.
So imho another approach may be better:
1. Override the success handler
Custom Success Action Field on FormFolder. You may add a single expression or a python script, which returns the right value.
Field description:
Use this field in place of a thanks-page designation to determine final action after calling your action adapter (if you have one). You would usually use this for a custom success template or script. Leave empty if unneeded. Otherwise, specify as you would a CMFFormController action type and argument, complete with type of action to execute (e.g., "redirect_to" or "traverse_to") and a TALES expression. For example, "redirect_to:string:thanks-page" would redirect to 'thanks-page'.
Your ./school_redirect needs only small changes to fit for this solution. Do not redirect yourself, just return the right id (of a page template/view/etc.)
2. Do not trigger the mail adapter
For this purpose the Mail-Adapter provides a field called Execution Condition.
A TALES expression that will be evaluated to determine whether or not to execute this action. Leave empty if unneeded, and the action will be executed. Your expression should evaluate as a boolean; return True if you wish the action to execute. PLEASE NOTE: errors in the evaluation of this expression will cause an error on form display.
Add a script or expression, which evaluates if the email needs to be triggered or not.
You may reuse the ./school_redirect script to compose the expression: python: here.school_redirect() != 'not_listed'

django make ModelChoiceField non-editable

I have a ModelForm with ModelChoiceField on it, and I need to make sure, that the initial value should stay fixed and we can't select any other one. It means, make this field non-editable, preserved, or smth like that. Making it CharField or just Field will not help here, I think, because I need an object from this field later for validation and processing.
Can someone, please, help?
self.fields['field_name'].widget.attrs['readonly'] = True
Turned out, I needed here the following code:
self.fields['field_name'].widget.attrs['disabled'] = 'disabled'
This option will remove the instance from the form fields when you submit the form.
self.fields['field_name'].widget.attrs['disabled'] = 'disabled'
Using this option you will disable de field to be edited and you can converse the init instance to be saved into the database.
algorithm = forms.ModelChoiceField(label='Algoritmo',queryset=Algorithm.objects.all(),to_field_name="name",widget=forms.TextInput(attrs={'readonly':'readonly'}))

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.

web2py FORM - lookup value by string field instead of ID

I have a model like this in web2py:
db.define_table('courses',
Field('course_name','string'))
db.define_table('auth_user',
....
....
Field('course_name',db.courses,label=T('Course Name'),
required=True,
requires=IS_IN_DB(db,db.courses.id,'%(course_name)s'),
If I display a form based on the auth_user table, the auth_user.course_name field is represented by a dropdown menu containing all of the courses in courses table. As expected, it displays them using the contents of the courses.course_name field, and not the courses.ID field (because of the string format representation in the IS_IN_DB requirement.
However, I'm trying to modify it such that instead of using a dropdown select menu, it just displays a text field. I'm expecting the user to be able to enter the name of a course, and the form will work properly as long as that name is a valid course_name.
To do that, I added a widget=SQLFORM.widgets.string.widget property to the auth_user.course_name field. That correctly displays a text box instead of the dropdown, but doesn't allow the user to enter a course_name. It works fine if a valid courses.id is entered (and displays the expected error message if it's not a valid ID).
However, I can't figure out how to make it accept a course_name instead of the ID. I could theoretically use the autocomplete plugin (which does indeed work), but the purpose of this is to allow the user to only submit the form if they know the valid course_name (it's kind of like a password).
Is this possible?
I figured this out by using a custom validation class (replacing the IS_IN_DB validator). For reference, this is what my validator looks like:
class COURSE_NAME_VALIDATOR:
def __init__(self, error_message='Unknown course name. Please see your instructor.'):
self.e = error_message
def __call__(self, value):
if db(db.courses.course_name == value).select():
return (db(db.courses.course_name == value).select()[0].id, None)
return (value, self.e)
I got the template for the validation class from the web2py manual (http://www.web2py.com/book/default/chapter/07#Custom-validators)

Categories