librelist archives

« back to archive

flask.ext.wtf returns only None

flask.ext.wtf returns only None

From:
Mic
Date:
2014-12-20 @ 14:38
Hello,
I am not able to retrieve with flask.ext.wtf from a TextField defined below
any value.

*    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">*
*      <div class="container">*
*          <div class="navbar-header">*
*            <button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">*
*            <span class="sr-only">Toggle navigation</span>*
*            <span class="icon-bar"></span>*
*            <span class="icon-bar"></span>*
*            <span class="icon-bar"></span>*
*            </button>*
*            <a class="navbar-brand" href="{{ url_for('index') }}">sdfs</a>*
*          </div>*
*          <!--/.navbar-header -->*
*          <div class="collapse navbar-collapse">*
*            <ul class="nav navbar-nav">*
*                <li id="home-url"><a href="{{ url_for('index')
}}">Home</a></li>*
*                <li id="about-url"><a href="#about">About</a></li>*
*                <li id="contact-url"><a href="#contact">Contact</a></li>*
*            </ul>*
*            <!--/.navbar-nav -->*
*            <form class="navbar-form navbar-right" method="get" action="{{
url_for('quick_search')}}" role="form">      *
*                <div class="input-group">*
*                    {{ form.property_id(class="form-control", type="text",
placeholder="Search")}}*
*                  <span class="input-group-addon">*
*                  <a href="#" class="my-tool-tip" data-toggle="tooltip"
data-placement="left" data-html="true" title="Tooltip here <BR> Hello"> *
*                  <span class="glyphicon glyphicon-info-sign"></span>*
*                  </a>*
*                  </span>*
*                </div>*
*                <!--/.input-group -->*
*            </form>*
*          </div>*
*          <!--/.nav-collapse -->*
*      </div>*
*      <!--/.container -->*
*    </div>*
*    <!--/.navbar -->*

The rote is defined in the following way:
*@app.route("/quick_search/", methods=("GET", "POST"))*
*@app.route("/quick_search/<property_id>")*
*def quick_search(property_id=None):*
*    form = QuickSearch()*
*    print form.property_id.data*

The form is defined in the following way:
*from flask.ext.wtf import Form*
*from wtforms import SelectField, StringField, IntegerField,
SelectMultipleField*

*class QuickSearch(Form):*
*    property_id = StringField()*

Why do I only get None from *form.property_id.data?*

Thank you in advance.

Cheers,

Michal

Re: [flask] flask.ext.wtf returns only None

From:
Matt Gushee
Date:
2014-12-22 @ 00:22
Hi, Michal--

First of all, I'm fairly new at this stuff myself, so I can't give you
definitive answers as to what is or is not possible with Flask and
WTForms.

Also, your question seems incomplete:

On Sat, Dec 20, 2014 at 7:38 AM, Mic <mictadlo@gmail.com> wrote:

> Why do I only get None from form.property_id.data?

Under what circumstances? I presume you mean that you ran your
application and attempted to submit the search form; if that is the
case, then there must surely be more code in your view function,
because it wouldn't work as written.

However, I was interested, so I played around with the code a bit, and
I can say a few things:

1) Your search form is submitted as a GET request. It appears to me
that WTForms only really supports POST. You can still use WTF to
render the form initially (as you have no doubt seen), but the form
object doesn't receive values submitted via GET. The simplest solution
that I know of is to submit the form with POST. You've probably seen
examples using this pattern:

    if form.validate_on_submit():
         # process input
    return render_template('myform.html', ....)

[according to the docs, validate_on_submit() includes checking that
the request method is POST]
So, if the request method is POST and the form contains valid data,
the data gets processed; otherwise, the user receives a blank form.
It's a nice simple way of handling a common use case, but maybe is not
always the right approach - see below.

2) The following code is a bit confused.

> @app.route("/quick_search/", methods=("GET", "POST"))
> @app.route("/quick_search/<property_id>")
> def quick_search(property_id=None):

So you appear to be supporting both GET and POST here. If you want to
follow the pattern mentioned above, then the first route is indeed the
right thing to do. In that case, you want:

    @app.route('/quick_search/', methods=['GET', 'POST'])
    def quick_search():

But it seems like you want the form to be submitted with GET. In that
case there is no reason to support POST at all, and your code would
look something like:

    @app_route('/quick_search/')
    def quick_search():
        property_id = request.args.get('property_id')
        if property_id is None:
            # render page with blank form
        else:
            # process form input

The following will not work in any case:

    @app.route('/quick_search/<property_id>')
    def quick_search(property_id=None):

In the above route, <property_id> matches a segment of the request
path. But when the query is submitted via an HTML form, the search
string is represented as part of a query string, not as a path
segment. I.e., let's say the user searches for "carnivorous spiders."
The above route would match '/quick_search/carnivorous%20spiders', but
the actual request will look like
'/quick_search?property_id=carnivorous%20spiders', and that doesn't
match. I don't think there is any route pattern that will match a
query string. You just have to extract it within the function, as
shown above.

3) Maybe you don't really need WTForms in this case. The form is very
simple, so I don't see much benefit in modeling it as an object. WTF
does provide validation and CSRF protection, but perhaps you don't
need those for a simple search function (except, I suppose, you want
to limit the length of the search string). And if you are using GET,
you don't have the use of the validation functions anyway. Just
because Flask provides a nice idiom based on submitting forms with
POST doesn't mean you have to do it that way. Indeed, according to
strict HTTP semantics, you *should* use GET, because a search request
(as that term is usually understood) is idempotent - i.e. its effects
are the same regardless of how many times you invoke it.

(I have a feeling someone is going to say that you really should use
POST for the sake of security. I am *not* a security expert; but I
would certainly agree that any real security considerations that may
be involved here (as opposed to generalized paranoia) would trump
HTTP-correctness)

Hope that helps.

--
Matt Gushee

Re: [flask] flask.ext.wtf returns only None

From:
Mic
Date:
2014-12-22 @ 01:30
Hi Matt,
Thank you for your great explanation and I will change the code and for
some security I will keep WTForms.

Michal



On Mon, Dec 22, 2014 at 10:22 AM, Matt Gushee <matt@salixmedia.com> wrote:

> Hi, Michal--
>
> First of all, I'm fairly new at this stuff myself, so I can't give you
> definitive answers as to what is or is not possible with Flask and
> WTForms.
>
> Also, your question seems incomplete:
>
> On Sat, Dec 20, 2014 at 7:38 AM, Mic <mictadlo@gmail.com> wrote:
>
> > Why do I only get None from form.property_id.data?
>
> Under what circumstances? I presume you mean that you ran your
> application and attempted to submit the search form; if that is the
> case, then there must surely be more code in your view function,
> because it wouldn't work as written.
>
> However, I was interested, so I played around with the code a bit, and
> I can say a few things:
>
> 1) Your search form is submitted as a GET request. It appears to me
> that WTForms only really supports POST. You can still use WTF to
> render the form initially (as you have no doubt seen), but the form
> object doesn't receive values submitted via GET. The simplest solution
> that I know of is to submit the form with POST. You've probably seen
> examples using this pattern:
>
>     if form.validate_on_submit():
>          # process input
>     return render_template('myform.html', ....)
>
> [according to the docs, validate_on_submit() includes checking that
> the request method is POST]
> So, if the request method is POST and the form contains valid data,
> the data gets processed; otherwise, the user receives a blank form.
> It's a nice simple way of handling a common use case, but maybe is not
> always the right approach - see below.
>
> 2) The following code is a bit confused.
>
> > @app.route("/quick_search/", methods=("GET", "POST"))
> > @app.route("/quick_search/<property_id>")
> > def quick_search(property_id=None):
>
> So you appear to be supporting both GET and POST here. If you want to
> follow the pattern mentioned above, then the first route is indeed the
> right thing to do. In that case, you want:
>
>     @app.route('/quick_search/', methods=['GET', 'POST'])
>     def quick_search():
>
> But it seems like you want the form to be submitted with GET. In that
> case there is no reason to support POST at all, and your code would
> look something like:
>
>     @app_route('/quick_search/')
>     def quick_search():
>         property_id = request.args.get('property_id')
>         if property_id is None:
>             # render page with blank form
>         else:
>             # process form input
>
> The following will not work in any case:
>
>     @app.route('/quick_search/<property_id>')
>     def quick_search(property_id=None):
>
> In the above route, <property_id> matches a segment of the request
> path. But when the query is submitted via an HTML form, the search
> string is represented as part of a query string, not as a path
> segment. I.e., let's say the user searches for "carnivorous spiders."
> The above route would match '/quick_search/carnivorous%20spiders', but
> the actual request will look like
> '/quick_search?property_id=carnivorous%20spiders', and that doesn't
> match. I don't think there is any route pattern that will match a
> query string. You just have to extract it within the function, as
> shown above.
>
> 3) Maybe you don't really need WTForms in this case. The form is very
> simple, so I don't see much benefit in modeling it as an object. WTF
> does provide validation and CSRF protection, but perhaps you don't
> need those for a simple search function (except, I suppose, you want
> to limit the length of the search string). And if you are using GET,
> you don't have the use of the validation functions anyway. Just
> because Flask provides a nice idiom based on submitting forms with
> POST doesn't mean you have to do it that way. Indeed, according to
> strict HTTP semantics, you *should* use GET, because a search request
> (as that term is usually understood) is idempotent - i.e. its effects
> are the same regardless of how many times you invoke it.
>
> (I have a feeling someone is going to say that you really should use
> POST for the sake of security. I am *not* a security expert; but I
> would certainly agree that any real security considerations that may
> be involved here (as opposed to generalized paranoia) would trump
> HTTP-correctness)
>
> Hope that helps.
>
> --
> Matt Gushee
>