Not a Valid Choice for Dynamic Select Field WTFORMS - python

I currently am creating a dynamic select field using WTFORMS, however it never submits and fails the validation with the following error.
Not a valid choice
My Field is created like this:
area = SelectField()
and in the view, i am grabbing the options from the db like so:
form = MytestForm()
form.area.choices = [(a.id, a.name) for a in Area.objects.all()]
It works however if i create static options.

My guess is that Area.id is a int - when data comes back from the client it is treated as a string by WTForms unless a callable is passed to the coerce keyword argument of the wtforms.fields.SelectField constructor:
area = SelectField(coerce=int)
Alternately, if you are using SQLAlchemy you could use wtforms.ext.sqlalchemy.fields.QuerySelectField (wtforms_sqlalchemy if you are using WTForms 3+):
area = QuerySelectField(query_factory=Area.objects.all,
get_pk=lambda a: a.id,
get_label=lambda a: a.name)

Here is how you can solve it without QuerySelectField.
Here is how I did:
years = [(str(y), y) for y in reversed(range(1950, 2013))]
years.insert(0, ('','year'))
year = wt.SelectField(choices=years)

a 3.01 Pullrequest adds the necessary argument:
validate_choice=True
To switch off the feature which more often than not seems to be buggy given that 33k views are on this question already and 52 upvotest as of 2022-01.
SelectField(coerce=int, validate_choice=False)
made me a happy camper

Remember to put also the available choices when setting up the Form class.
class MyForm(FlaskForm):
sel = SelectField(coerce=int, label="MyLabel", choices=my_choices)
....
where choices can be something like this:
my_choices = [x.id for x in get_products()]
The exception is arised in function pre_validate in script /wtforms/fields/core.py when the validate_on_submit() functions is called

Related

how do i change de default value of a model from a view function on django?

This is my code
models.py
...
class Cobranza(models.Model):
profesional = models.ForeignKey(Profesional)
porcentaje_aporte = models.IntegerField(default=30)
...
what i want to do is changing that default value of "porcentaje_aporte" to another number using a function in the views.py
Is that possible? if it is so, how can i do it? any alternative?
I have looked for a solution in django docs and haven't got anything useful.
Also i tryed with the following function but it didn't worked
def cambiarAporte(request):
nuevoAporte = request.POST['aporte']
Cobranza.porcentaje_aporte.default = nuevoAporte;
basically i want this
porcentaje_aporte = models.IntegerField(default=30)
to become this
porcentaje_aporte = models.IntegerField(default=*new number*)
so any object of this class that i make in the future, get that new default value
You can achieve what you want by doing:
Cobranza._meta.get_field('porcentaje_aporte').default = nuevoAporte
This operation is dangerous as it will alter the field data for other requests.
What you should use is ModelForm with initial data.
This is how you should implemet this, hopefully I understood what you want:
Use a file or database to store the value in the view and retrive it here as the default for the field using the following method:
def get_default_percentaje():
'''
Read the value from where it is stored. File or database.
'''
try:
fh = open('file_with_value.txt')
data = int(fh.read().strip())
fh.close()
except:
data = 30
return data
class Cobranza(models.Model):
profesional = models.ForeignKey(Profesional)
porcentaje_aporte = models.IntegerField(default=get_default_porcentaje)

How can I disable the wtforms SelectField choices validation?

I have a wtforms form
class MyForm(Form):
names = SelectField('name', choices=[])
The choices of names field is built dynamically and rendered in the template by an ajax call. When I submit the form, it raises an exception "not a valid choice". I don't want the form to validate the choices of names field for me. How can I disable the validation?
I did something like this to step around the SelectMultipleField validation in WTForms. It should work the same way with a plain SelectField
class NonValidatingSelectMultipleField(SelectMultipleField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
I simply override the built-in validation.
I was stuck with the same issue. The solution provided by Xealot is great. I found that there is an option to set validation to False using validate_choice=False. I have included an example of both the solutions below.
class NonValidatingSelectField(SelectField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
class MyForm(Form):
names = NonValidatingSelectField('name')
names2 = SelectField('name2', validate_choice=False)
By "I don't want the form to validate the choices", I assume you actually mean "I'm going to do it myself later and doubt the form's ability to do it correctly".
But you are in luck! You can subclass an existing form to add choices dynamically.
class MyForm(Form):
# other fields...
def some_handler(request):
name_choices = build_name_choices()
class RealForm(MyForm):
names = SelectField('name', choices=name_choices)
form = RealForm(request.GET)
form.validate()
This also saves you from the tedium of merging the form's validation and error messages with those you generate yourself later.

How to add parameters to urls in Django?

I have a view that filters the field called "defaultfieldname" in a certain object_list. What I want to do is to adapt it to pass the name of the field as as parameter in urls.py, so I could use different urls for different fields.
I am not sure which way would be easier:
url(r'^calendar/birthday/$', login_required(MonthCalends.as_view(model=Person)), name='bday_list', filter_field="birthdate"),
url(r'^calendar/deathday/$', login_required(MonthCalends.as_view(model=Person)), name='dday_list', filter_field="deathdate"),
or
url(r'^calendar/birthday/$', login_required(MonthCalends.as_view(model=Person, filter_field="birthdate")), name='bday_list'),
url(r'^calendar/deathday/$', login_required(MonthCalends.as_view(model=Person, filter_field="deathdate")), name='dday_list'),
Then I have a view:
class MonthCalends(ListView):
template_name='month_list.html'
## Sets default fieldname value
filter_field = "defaultfieldname"
...rest of code
The param in urls.py should overwrite the "defaultfieldname" on the view, but I don't know how to get the filter_field from the urls.py in the view. Any help?
Thanks!
The arguments you send with as_view are set on the MonthCalends object. That means filter_field is available as self.filter_field. Assuming you have defined the get method you could do as follows:
class MonthCalends(ListView):
template_name='month_list.html'
## Sets default fieldname value
filter_field = "defaultfieldname"
def get(self, request, *args, **kwargs):
try:
# if the filter field was sent as an argument
filter_field = self.filter_field
except:
# else revert to default
filter_field = MonthCalends.filter_field
# ...rest of code
For a more full explanation check the Django class based views documentation.
You may just use one url, that triggers the second part of your url:
url(r'^calendar/(\w+)$', login_required(MonthCalends.as_view(model=Person)), name='bday_list'),
Then you may access it using self.args[0]
And in case you just permit two different types for filter_field, you may just raise an exception later in the class that you have read self.args[0].
Of course, you may use more readable syntax in the regex like:
r'^calendar/(?P<type>\w+)$'
In this case you can access it using self.kwargs['type'].
Anyway, using regex groups seems much neater.

Django MultipleChoiceField don't update initual data in Form overload

I am trying to load default values for a MultipleChoiceField in a Form overload.
So if I give the initial variable inside the MultipleChoiceField it works:
class UserPreferences(forms.Form):
my_form = forms.MultipleChoiceField(
widget = forms.CheckboxSelectMultiple(),
choices = MY_CHOICES,
initial = MY_INITIAL_DICT)
But if I try to get initial values from the database related to a user and update initial as follows, my_form initial variable doesn't change and stays empty:
class UserPreferences(forms.Form):
my_form = forms.MultipleChoiceField(
widget = forms.CheckboxSelectMultiple(),
choices = MY_CHOICES)
def __init__(self, user):
initial = make_my_dict_from( MyModel.objects.filter(user=user) )
super(UserPreferences, self).__init__(initial, empty_permitted)
If I do this with a simple ChoiceField it works, but not with MultipleChoiceField.
Thanks for any suggestions!
try this:
super(UserPreferences, self).__init__(initial=initial)
You need to use keyword arguments like this. Python does not look at your variable names, and uses the right slots.
Where does "empty_permitted" come from? Your example does use, but not define this variable.

App engine datastore query issue

I have a weired problem with couple of queries I am trying to run.
I have built a method which returns a tuple of result from the query-
def get_activeproducts():
query = Product.gql("WHERE active = True")
choices = []
for obj in query:
choices.append((str(obj.key()), obj.name))
return choices
The problem is, the result is same for each call. Even if products are deleted or changed to 'False' in the product attribute 'active'. The result will be refreshed only when I restart the sdk server. In production, it just doesnt change till I change versions.
I have seen similar issue with one more query where the query property is BooleanProperty.
Any idea on how this could be fixed?
EDIT:
I am using the method in a tipfy application. It is used to populate a select field in wtforms. 'choices' basically takes in a list of tuples (value, name) pair.
class InvoiceForm(Form):
product = SelectField('Product', choices=get_activeproducts())
I dont have any issue with editing. WHen I check it from the admin end, I can see that certain products are set to 'False'. And even if I empty(delete) the whole list of products, I get the same list I got the first time.
I am not using caching anywhere in the application.
Your class definition is getting cached by the App Engine runtime when an instance is started, with the default set to what it was when the instance started. To make the choices dynamic, you need to set them at runtime.
Example from the wtforms (which IIRC is what tipfy is using) docs; will need to be adjusted for App Engine queries:
class UserDetails(Form):
group_id = SelectField(u'Group', coerce=int)
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
when you create your form, the function is called once.
you can overload the form __init__.py function to do this cleanly
class InvoiceForm(Form):
product = SelectField(u'Group', choices=[])
def __init__(self, product_select, *args, **kwargs)
super(InvoiceForm, self).__init__(*args, **kwargs)
self.product.choices = select_dict
----
form = InvoiceForm(product_select=get_activeproducts())

Categories

Resources