Giter Club home page Giter Club logo

wtforms's Introduction

WTForms

WTForms is a flexible forms validation and rendering library for Python web development. It can work with whatever web framework and template engine you choose. It supports data validation, CSRF protection, internationalization (I18N), and more. There are various community libraries that provide closer integration with popular frameworks.

Installation

Install and update using pip:

pip install -U WTForms

Third-Party Library Integrations

WTForms is designed to work with any web framework and template engine. There are a number of community-provided libraries that make integrating with frameworks even better.

  • Flask-WTF integrates with the Flask framework. It can automatically load data from the request, uses Flask-Babel to translate based on user-selected locale, provides full-application CSRF, and more.
  • WTForms-Alchemy provides rich support for generating forms from SQLAlchemy models, including an expanded set of fields and validators.
  • WTForms-SQLAlchemy provides ORM-backed fields and form generation from SQLAlchemy models.
  • WTForms-AppEngine provides ORM-backed fields and form generation from AppEnding db/ndb schema
  • WTForms-Django provides ORM-backed fields and form generation from Django models, as well as integration with Django's I18N support.
  • WTForms-Bootstrap5 provides Bootstrap 5 favor renderer with great customizability.
  • Starlette-WTF integrates with Starlette and the FastAPI framework, based on the features of Flask-WTF.
  • Bootstrap-Flask Bootstrap-Flask is a collection of Jinja macros for Bootstrap 4 & 5 and Flask using Flask-WTF.

wtforms's People

Contributors

alanhamlett avatar andymccurdy avatar azmeuk avatar bittin avatar britonad avatar cjmayo avatar crast avatar davidism avatar doobeh avatar ftm avatar giandrop avatar greyli avatar gumuncle avatar jeanphix avatar jirivrany avatar lepture avatar mblomdahl avatar melihucar avatar mfa avatar moraes avatar mrshu avatar pandermusubi avatar prencher avatar russellfinlay avatar sblondon avatar sohalt avatar vladev avatar vsajip avatar whb07 avatar yggi49 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wtforms's Issues

IntegerField behaves strange

# coding: utf-8

import wtforms

wtforms.__version__
>>> 1.0.5

class Test(wtforms.Form):
    t = wtforms.fields.IntegerField('test', validators=[wtforms.validators.required(), wtforms.validators.number_range(min=3, message='unacceptable.')])

f = Test(t=1)
f.validate()
>>> False

f.errors
{'t': ['unacceptable.']}

f = Test(t=0)
f.validate()
>>> False

f.errors
{'t': [u'This field is required.']}

f = Test(t='hello')
f.validate()
>>> True

IntegerField accepts string(?), and "required" validator evaluates "0 (int)" as False as it seems.

Am I doing something wrong?

"title" field value

wtforms '1.0.5'
python Python 2.7.5+

If "title" value wasn't sent I get in model_form.title.data something like "<built-in method title of str object at 0x7fa3faee0508>"

forms.py

class ModelForm(Form):
    title = StringField(validators=[validators.Length(3, 100), validators.Required()])

views.py

def main(request)
    model_form = ReviewForm(request.POST, '', 'ReviewForm')
    if model_form.validate():
        # to do something
    return {'model_form': model_form}

template.pt

<input type="text" name="ModelForm-title" value="model_form.title.data" />

(From bitbucket) Support locales in DecimalField

From original author:

I'm building an app with wtforms which needs to support different locales.

The DateField for example has a nice format option to customize the format.

This is not the case for specifcally the DecimalField (and FloatField). It would be nice to be able to customize the decimal separator symbol used to mark the border between the integral and the fractional parts of a decimal.

Can't set default value on a dynamic SelectField

Looks like trying to assign a default value to a SelectField that has been initialized without choices=[…] and default=… parameters doesn't work - the assigned default is silently ignored.

QuerySelectMultipleField reorders saved data

The implementation of the saving of data from a multi-select field seems incorrect, but I wanted to get some understanding of the motivation. In
https://github.com/wtforms/wtforms/blob/master/wtforms/ext/sqlalchemy/fields.py#L175
the implementation is to do self._formdata = set(valuelist). The problem is that the order that is defined on the front-end is then destroyed when saving to the database. I realize this could be done in other ways (an 'order' field, for example) but an array should be expected to retain order I feel.

Take the example of a music playlist or slideshow. You can move items around and have an order that you want. But set() destroys that order. If the issue is to remove duplicates that's also not the domain of the formdata - that should be handled by the logic on the model explicitly. Wonder if I'm perhaps misunderstanding something.

My suggestion is that the list be retained as it is provided to wtforms from the front-end.

(Also see https://github.com/mrjoes/flask-admin/issues/466)

Using SQLAlchemy model_form generates "Csrf Token:" label for the hidden field

Hi, I'm using Flask with WTForm and SQLAlchemy. When I use the model_form extension it correctly generates the model's fields with a hidden CSRF token. The problem is the CSRF label is visible. The net effect is a visible label for a hidden CSRF field. My preference would be that model_form would not create a label when generating a hidden fields.

Here is my code

from models import Tag
from flask_wtf import Form
from wtforms.ext.sqlalchemy.orm import model_form

TagForm = model_form(Tag, base_class=Form)

@app.route('/tags')
def tags():
    form = TagForm()
    tags = Tag.query.all()
    return render_template('tag.html', form=form, tags=tags)

Jinja2 Template the produces the problem.

<form method="POST" action="">
{% for field in form %}
    <tr>
        <th>{{ field.label }}:</th>
        <td>{{ field }}</td>
    </tr>
{% endfor %}
</form>

jinja2 workaround that removes the label while preserving the Flask CSRF token.

<form method="POST" action="">
 {{form.hidden_tag()}}
{% for field in form if field.widget.input_type != 'hidden' %}
    <tr>
        <th>{{ field.label }}:</th>
        <td>{{ field }}</td>
    </tr>
{% endfor %}
</form>

Allow None values for StringField's data when wtforms.validators.Optional()

Currently, a wtforms.fields.StringField coerces a None value into empty string, even when the field is optional.

Sometimes an input is allowed to be None (like when the sql column is nullable).
Adding an argument to StringField called nullable should fix this. When nullable is set to True the data is not converted from None into an empty string.

DecimalField will not accept null input?

I've included a DecimalField in my form and I haven't set any DataRequired validators. But if I try to submit the form without a DecimalField input, I get the validation error:

Not a valid decimal value

Python 3: Class attribute not valid

Using class as the name of an argument in Python 3 is not allowed. Unfortunately, that affects things like this:

form.field(class="form-control")

It triggers a syntax error. This has been tested with Python 3.3.3.

Temporary fix for those also encountering this error:

# In the wtforms/widgets/core.py file, locate
# Input.__call__ and add the following code
# before the HTMLString line.

if 'css_class' in kwargs:
    kwargs['class'] = kwargs.pop('css_class')

# Now you can use css_class instead of class.  Example:

form.field(css_class='form-control')

Note: This fix also works on other widgets. For labels, the code would also have to be added to wtforms/fields/core.py's Label.call function where it must come before the html_params line.

field.Select None value coerces into unicode

from wtforms import Form, SelectField

class TestForm(Form):
select = SelectField('Test field', choices=[])

f = TestForm()

print(type(f.select.data))
<type 'unicode'>

print(f.select.data)
None

I suggest, that it's a bug, because is None checking failed

Requered validator refuses 0 for IntegerField

It was very surprising for me that 0 fails validation for required IntegerField.

Here is test:

import unittest
from wtforms import Form, IntegerField
from wtforms.validators import required


class DummyPostData(dict):
    def getlist(self, key):
        v = self[key]
        if not isinstance(v, (list, tuple)):
            v = [v]
        return v


class IntegersForm(Form):
    number_0 = IntegerField('Number', [required()])
    number_1 = IntegerField('Number', [required()])


class IntegerFieldTestCase(unittest.TestCase):
    def test_0_passes_required(self):
        data = DummyPostData({'number_0': 0, 'number_1': 1})
        form = IntegersForm(data)
        self.assertTrue(form.validate(), form.errors)


if __name__ == '__main__':
    unittest.main()

And it's output:

F
======================================================================
FAIL: test_0_passes_required (__main__.IntegerFieldTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_integer_field.py", line 23, in test_0_passes_required
    self.assertTrue(form.validate(), form.errors)
AssertionError: {'number_0': [u'This field is required.']}

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

StopValidation not caught during process_formdata

If ValidationError is raised during process_formdata, it is correctly caught and added to .errors. StopValidation is not caught; therefore there is no way to say "I couldn't process the form data, don't run any validators." Right now I need to have a custom validator check if field.data is None: raise StopValidation('couldn't process data').

This is somewhat demonstrated in your own code for dateutil DateTimeField. The field raises a ValidationError if it couldn't parse the data during processing. A form using this field with validators will need to verify that the data is not None before doing anything else that assumes a datetime. This could be handled better if the field raised StopValidation and wtforms handled it correctly.

QuerySelectMultipleField reorders saved data

Sorry to resurrect a closed issue, but my core question is not addressed. This is related to #53 ...

The response there was:

It actually keeps the data in the order that it is in the query. _formdata is an internal reference value which is used in the .data property accessor which iterates the object list in the order it is in the query, and modifies _formdata as it does so.

The resulting ordering is the same ordering that is in the query, and also the same ordering that is in the UI.

Maybe I'm missing something in my understanding. The following illustrates:

>>> valuelist = ['a', 'c', 'd', 'b']
>>> set(valuelist)
set(['a', 'c', 'b', 'd'])
>>> valuelist = ['bill', 'bob', 'jill', 'joan']
>>> set(valuelist)
set(['bob', 'bill', 'joan', 'jill'])
>>> valuelist = [1, 5, 8, 9, 3, 6]
>>> set(valuelist)
set([1, 3, 5, 6, 8, 9])

Is there something special about valuelist in fields.py as you provide it that would retain order in set()?

Allow actual length in message for Length validator

I'm using the Length validator where the max is 3000. It is annoying for users because they can't easily figure out how far over the max they are. Would be nice to add this to the Length validator by simply changing

        raise ValidationError(message % dict(min=self.min, max=self.max))

to

        raise ValidationError(message % dict(min=self.min, max=self.max, actual=l))

I know I can do a custom validator, but it is such a small change and a feature that I think many will appreciate.

Validation Issues when processing form kwargs

I am attempting to create a form by calling process and passing in the data as kwargs as follows:

data = {
    'first_name': 'Jim',
    'last_name': 'Bob',
    'email': '[email protected]',
    'organization_name': 'ACME Inc.'
}
form = SignupForm()
form.process(**data)

Once processed, I can verify the data has worked by accessing values such as form.email.data. However, InputRequired validator only checks for raw_data. Is there any other way to pass in the data that would allow InputRequired to work without passing in a request object? Mostly asking as I am attempting to test my Forms without using requests.

widgets.Select should coerce value to text_type

From widgets/core.py:

@classmethod
def render_option(cls, value, label, selected, **kwargs):
    options = dict(kwargs, value=value)
    if selected:
        options['selected'] = True
    return HTMLString('<option %s>%s</option>' % (html_params(**options), escape(text_type(label))))

Because html_params treats value=True specially, this makes it impossible to use True as a choice in a SelectField.

Perhaps this code is correct:

    options = dict(kwargs, value=text_type(value))

String field does not support object data

In process_formdata, if any form data is provided (even if raw field data is not available), the data attribute will be set to the empty string – even when object data is available:

def process_formdata(self, valuelist):
    if valuelist:
        self.data = valuelist[0]
    else:
        self.data = ''

If object data is available, self.data would have that data, so it should not be cleared:

def process_formdata(self, valuelist):
    if valuelist:
        self.data = valuelist[0]

model_form doesn't properly generate a form when using flask_wtf

When I create an instance of model_form with flask_wtf.Form it doesn't seem to be properly initializing the methods for that generated form (see below).

As a side issue, for some reason populate_obj sets values of my object to 'None' if they're are None. Perhaps that's caused by my DATE_FORMAT updates below.

from flask_wtf import Form
from wtforms import DateField
from wtforms.ext.sqlalchemy.orm import model_form
from models import Resident
from settings import DATE_FORMAT

ResidentForm = model_form(Resident, Form)
for key, value in ResidentForm.__dict__.iteritems():
    if getattr(value, 'field_class', None) is DateField:
        value.kwargs['format'] = DATE_FORMAT


@app.route('/post_resident', methods=['POST'])
@app.route('/post_resident/<int:resident_id>', methods=['POST'])
def post_resident(resident_id=None):
    form = ResidentForm(request.form)
    resident = None
    if resident_id:
        resident = Resident.query.get_or_404(resident_id)
    else:
        resident = Resident()
    if form.validate(): <-- NO VALIDATE ON SUBMIT
        for key in form.data:  <-- UGLY HACK BECAUSE VALIDATE PLACES 'None' VALUES INTO MY OBJECTS
            if form.data[key] == 'None':
                del form.data[key]
        form.populate_obj(resident)
        db.session.add(resident)
        db.session.commit()
        return redirect(url_for('index'))
    else:
        return index(form, resident_id=resident_id)

Exception on coercing None to int

Very minimal example:

from wtforms import Form, SelectField, validators
from werkzeug.datastructures import MultiDict

class TestForm(Form):
    field = SelectField('field', [validators.Required()], coerce=int, choices=[(1, 1)])

form = TestForm()
form.process(MultiDict({'field': ''}))
form.validate()
#False
form.process(MultiDict({'field': None}))
#Exception
form.validate()

I think it would be reasonable for process to succeed and for validation to fail. It is very inconvenient because it requires extra layer of try/except instead of having it all in once place (validate).

[feature request/question] Form wise errors

On form validation I want to raise errors such as: "at least a field must be filled" which do not belong to one field, but to the form as a whole.

Would it be possible to raise have them on the form? (I'm currently using a hidden field and validating these kind of errors on it).

"Not a valid choice" message isn't customizable in SelectField

SelectField and RadioField will error with "Not a valid choice" if no option is selected. This message isn't customizable, and since it's raised by the pre-validation handler, it cannot be customized via the Required() validator.

SelectField and RadioField should accept an additional parameter for this message.

Model_form & populate_obj and foreignkey

I'm having an issue with a model_form and populate_obj not updating foreignkey fields. I'm accepting data via JSON and using integers as the value.

I'm using SQLAlchemy for the models. Is there anything special you need to do to get a model_form to update a field that is a foreignkey?

Many thanks!

Modifying form instances modifies the form class!

This is a bug that had me scratching my head for quite a few hours... I have a use case where I want some form instances to have different validation rules than other instances. Rather than create 2 different clasess, I figured I could just modify the validation rules at runtime.

Surprisingly, this doesn't work as expected. Modifying one of the validators on the instance modifies the validator for all other instances as well!

Here's a test case:

import wtforms

class FooForm(wtforms.Form):

    foo = wtforms.TextField(
        "Foo",
        description="This is a foo!",
        validators=[
            wtforms.validators.Optional(),
            wtforms.validators.Regexp(
                r'^[A-Za-z_\.-]+$',
                message="Foo can only contain alphanumeric " \
                        "characters, underscore, hypen, and dot."
            )
        ],
    )

if __name__ == "__main__":

    print(wtforms.__version__)

    # Let's start with a normal FooForm:
    foo1 = FooForm()
    print(foo1.foo.validators[0]) # Optional!

    # Now let's make a FooForm instance where foo is required:
    foo2 = FooForm()
    foo2.foo.flags = wtforms.fields.Flags()
    foo2.foo.flags.required = True
    foo2.foo.validators[0] = wtforms.validators.Required()
    print(foo2.foo.validators[0]) # Required!

    # Back to normal FooForm:
    foo3 = FooForm()
    print(foo3.foo.validators[0]) # Required. WTF?

This test case produces the following output with the lastest pip install version of WTForms:

mhaase@localhost:~$ python wtforms-test.py 
1.0.5
<wtforms.validators.Optional object at 0xfb94d0>
<wtforms.validators.Required object at 0xfb9e90>
<wtforms.validators.Required object at 0xfb9e90>

And produces this output on WTForms master (2014-04-05):

mhaase@localhost:~$ python wtforms-test.py 
2.0dev
<wtforms.validators.Optional object at 0x7f7dc3c64fd0>
<wtforms.validators.Required object at 0x7f7dc3c6b910>
<wtforms.validators.Required object at 0x7f7dc3c6b910>

This behavior results from the fact that UnboundField shares instances inside kwargs with the bound fields that it creates. Assuming you agree that this is suprising and undesirable behavior, the simple fix is to deep clone the kwargs in UnboundField.bind().

I can make a pull request if you like. Let me know!

Obtaining form data from multiple sources

This is a feature request.

I'm using WTForms to validate JSON data. I know it's meant to process HTML forms, but it's nearly perfect for my JSON validation needs and I prefer its simplicity and syntax to other validation options.

I'd like to be able to provide multiple data sources to a form, and have the form combine them for validation purposes. The reason for this is to facilitate processing PATCH requests where only changed data is submitted.

For example, I'd like to do this:

MyForm validates two required fields: name, age

patch_data = dict(name="Joe")
existing_record_data = dict(name="Bob", age=30)
form = MyForm(formdata=patch_data, data=existing_record_data)

form.validate() would be true here, because existing_record_data contains age. I'd like a way to determine get just the patch_data fields back as well (after e.g. conversion to ints, etc.). I can already get patch data back via WTForms-JSON, but it'd be nice if that feature were in the core WTForms.

The docs for WTForms used to indicate that this sort of thing was possible, but have since been updated to clarify that if any formdata is provided, then obj and data are ignored.

If these are changes that you'd be willing to incorporate, I'd be willing to work on implementation.

html_params needs to handle False properly

properly = skipping the attribute. Right now I need to write nasty code like this in my templates which is very non-DRY:

    {% if field.flags.required %}
        {{ field(required=true, class='form-control', placeholder=field.label.text, **kwargs) }}
    {% else %}
        {{ field(class='form-control', placeholder=field.label.text, **kwargs) }}
    {% endif %}

SelectField doesn't coerce in pre_validate

I ran into this when I had a SelectField setup with a coerce of uuid.UUID. Because SelectField.pre_validate doesn't coerce the value from the choices, the validation failed. Similar to iter_choices, pre_validate should likely coerce on the values, something along these lines:

    def pre_validate(self, form):
        for v, _ in self.choices:
            if self.data == self.coerce(v):
                break
        else:
            raise ValueError(self.gettext('Not a valid choice'))

The other option would be to specify that the first part of choices should always be coerced already, but the rest of the code doesn't appear to assume that.

Associating errors with FieldList positions

I'm using WTForms v1.0.5.

Is there any way to determine which element in a FieldList has generated an error? If I submit two elements to a FieldList, and the second element has errors, myform.errors.myfield ends up with [["some error"]] in it. It's correctly nesting lists here, which I'm happy to see, but there's no placeholder for the valid first element, so I have no way of associating the error with the second element.

I'd like to see something like [None, ["first error on second element", "second error on second element"]].

There's also an open Stack Overflow question on this.

Thanks!

FieldList loses first element when form is filled from an object

When a form containing FieldList is filled from an object and then fed with form data, FieldList loses the first element.

>>> from wtforms import Form, FieldList, TextField
>>> from tests.common import DummyPostData
>>> 
>>> class FooForm(Form):
...     foo = FieldList(TextField())
... 
>>> class Foo(object):
...     def __init__(self, foo):
...         self.foo = foo
... 
>>> obj = Foo(['alpha', 'beta'])
>>> form = FooForm(obj=obj)
>>> form.foo.data
['alpha', 'beta']
>>> 
>>> form.process(formdata=DummyPostData({'foo-0': ['alpha'], 'foo-1': ['beta']}))
>>> form.foo.data
[u'', 'beta']

(From Bitbucket) Filters execution into field

Hi.

wtforms/fiels/core.py
starts on line 263

#!python

        for filter in self.filters:
            try:
                self.data = filter(self.data)
            except ValueError as e:
                self.process_errors.append(e.args[0])

Why filters continue execution even after ValueError exception? Filter suppose to receive data from previous filter i.e. it expect data in some "format"... but if ont filter in chain drop ValueError, this mean - it can't process/convert data... So next filter in chain receive data in unexcpected format.

I can't figure out any usage case where this logic is comfortable.

I think this variant will be much more comfortable:

#!python

        try:
            for filter in self.filters:
                self.data = filter(self.data)
        except ValueError as e:
            self.process_errors.append(e.args[0])

Or refuse from use a chain of filters and use only one filter instead.

NullableIntegerField

I would like a field, which if left blank is None, and otherwise behaves like IntegerField.

As a workaround, we made this class:

class NullableIntegerField(IntegerField):
    """
    An IntegerField where the field can be null if the input data is an empty
    string.
    """

    def process_formdata(self, valuelist):
        if valuelist:
            if valuelist[0] == '':
                self.data = None
            else:
                try:
                    self.data = int(valuelist[0])
                except ValueError:
                    self.data = None
                    raise ValueError(self.gettext('Not a valid integer value'))

Is there a better way to do this? Should I make some tests and a pull request?

TextField with validator.optional() does not treat default if there is input in other Form fields

Hi

my task is to create text field which:

  1. if entered, must be one of predefined values;
  2. if omitted, must be replaced with given default value;

Here is example for TextField and IntegerField, that shows obscure behaviour.

from wtforms import Form, TextField, IntegerField, validators


class OptionalAndDefaultForm(Form):
    text_field = TextField('Text with optional and default',
                           [validators.optional(),
                            validators.AnyOf('AB')],
                           default='A')
    integer_field = IntegerField('Integer with optional and default',
                                 [validators.optional()],
                                 default=3)


class DummyPostData(dict):
    def getlist(self, key):
        v = self[key]
        if not isinstance(v, (list, tuple)):
            v = [v]
        return v


form1 = OptionalAndDefaultForm(DummyPostData({}))
form1.validate()
print form1.errors, form1.data

form2 = OptionalAndDefaultForm(DummyPostData({
    'integer_field': '',
}))
form2.validate()
print form2.errors, form2.data

outputs

{} {'text_field': 'A', 'integer_field': 3}
{} {'text_field': u'', 'integer_field': None}

You can mention, that integer_field has some tricky logic too.

SA model_form ignore obj

SQLAlchemy model_form factory ignore obj key argument,

have to insert form._obj = validate_obj before form.validate()

How to cleanly realize inline editing?

I would like to make some forms able to validate and submit data on single form fields without major hacks. This is probably out of scope for wtforms (although it would be awesome if it were added!) but still what would be an OK way to realize this? Also, this probably has some security implication (CSRF).

ext.sqlalchemy.orm.model_form, not able to edit existing db rows

Hi everyone,

I understand that this extension is now deprecated, but I have been advised that bug fixes will still be made.

I am having an issue editing existing database entries using model_form. The form populates correctly with the data of the existing entry, but when trying to edit the db object and resubmit the form, I get an 'Already exists' error.

I have made a simple example to illustrate the issue.

https://github.com/iaculch/wtf-sqlalchemy-issue

Simply run initdb.py and then buggy.py

Thanks

New SelectField implementation

Problem

Now that WTForms 2.0 is approaching I think its time to rethink the whole implementation of SelectFields. Also as wtforms.ext.sqlalchemy will be deprecated and the functionality will be moved to WTForms-Alchemy, I think its time to rethink QuerySelectFields.

The problems I see with the current implementation:

  1. There can be only one value besides queried values in QuerySelectField (the blank value). One can not customize this value nor can he add more of these values in QuerySelectField. I've had cases where I needed multiple non-queried values for QuerySelectField. A classic example is a filter where one value represents 'No selection', one value represents 'All' and the other values are queried values.
  2. SelectFields do not allow the rendering of optgroups
  3. QuerySelectFields do not allow the rendering of optgroups
  4. SelectFields do not allow lazy-evaluated choices (callables as choices)

Proposal

SelectFields should use special Choices classes internally which encapsulate the logic behind these iterables. This would allow us to:

  1. Get rid of QuerySelectField and QueryMultipleSelectField and instead use:

    SelectField(choices=QueryChoices(...))
  2. The old constructor syntax would still work. For example:

    SelectField(choices=[(..), (..)]) 
  3. The Choices classes would support nesting and the new SelectField would render nested choices as optgroups.

  4. One could combine regular choices to QueryChoices.

    SelectField(
       choices=Choices(
           [
               ('', 'No selection'),
               ('all', 'All'),
           ]
       ) + QueryChoices(query_factor=lambda: session.query(User))
    )

A working example is available at: https://github.com/kvesteri/wtforms-components/blob/features/choices/wtforms_components/fields/select.py

An example implementation of QueryChoices:

def get_pk_from_identity(obj):
    cls, key = identity_key(instance=obj)
    return ':'.join(map(six.text_type, key))


def identity(func):
    return func


def labelize(func):
    if func is None:
        return identity
    elif isinstance(func, six.string_types):
        return operator.attrgetter(func)
    return func


class QueryChoices(Choices):
    def __init__(self, query_factory, get_pk=None, get_label=None):
        if get_pk is None:
            if not has_identity_key:
                raise Exception(
                    'The sqlalchemy identity_key function could not be '
                    'imported.'
                )
            self.get_pk = get_pk_from_identity
        else:
            self.get_pk = get_pk

        self.get_label = labelize(get_label)
        self.query_factory = query_factory
        self._object_list = None

    @property
    def object_list(self):
        if self._object_list is None:
            query = self.query_factory()
            self._object_list = list(
                (six.text_type(self.get_pk(obj)), obj) for obj in query
            )
        return self._object_list

    @property
    def choices(self):
        for pk, obj in self.object_list:
            yield pk, self.get_label(obj), obj

Don't call filters when no field data submit.

I use Flask-WTForms in my Flask project.

I add a filter to email field and pass signup form instance to template. When user enter the signup page via GET, it issues NoneType error...

So I think do not call filters when no field data submit is a good idea!

Question on 'DataRequired' behaviour

Hi everyone,

I'm used to work with WTForms for a long time now and I find a, AFAIK, a weird case of form validation :

from wtforms.validators import DataRequired
from wtforms.fields.core import DateField
from wtforms.form import Form

class TestForm(Form):
    date = DateField('Test', [DataRequired()])

form = TestForm(date='20/03/06')
form.validate()
>>> False

form.errors
{'date': ['This field is required.']}

Or I assume here the main error is that the format for the date is incorrect, which is raised with the process validation of the 'DateField' field, which also set field's data to None when error.

But DataRequired delete all previous error when no data and send his own message.

It's clearly what we want from 'DataRequired' when we read the code, but that seems a bit weird for me. Did I not see an obvious point that would explain this behaviour ?

I'm using wtforms 1.0.5

Add a Field kwarg attribute to allow processing of reserved-name request parameter

You cannot use Python- or wtforms-reserved names for Fields. This limits wtforms' ability to work with pre-exisiting libraries and APIs. For example, the Google App Engine's Users API has a function called create_login_url. It adds a destination URL passed as a query string parameter named 'continue'. I wanted to emulate this API and process the 'continue' parameter in a wtform, but you cannot write something like:

continue = wtforms.fields.StringField(widget=wtforms.widgets.HiddenInput())

And there is no way that I know of to have wtforms use 'continue' request parameter values when processing form data. What I suggest is to add an optional kwarg and a read-only attribute on all Fields, so you could write:

dest_url = wtforms.fields.StringField(
   widget=wtforms.widgets.HiddenInput(), param_name='continue')

Then the "name" of the field is 'dest_url', but to the HTML world the field uses 'continue'. The field above would process any query string or POST data that had 'continue' parameter data and generate this HTML:

<input type="hidden" name="continue" value="http://path/to/next/url" />

placeholder support

It would be nice if you could specify a placeholder attribute on all supported html5 input types, specifically text, search, url, tel, email, and password input types.

GroupedQueryMultiSelectField

Today I needed a GroupedQueryMultiSelectField and wrote one. Is there any interest in incorporating such a thing into wtforms? I would set up a PR if you are interested and tell me how exactly you would want to incorporate it.

Check the gist below for the actual code:

https://gist.github.com/alexex/8607697

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.