lundi 5 mai 2014

A short example of Flask capacity with WTF

Hi there,

I'm normally "another french fucking blogger" but today, I was coding an uninteressant thing and I got more problems than I should ; that's why I'm writing this post : to explain how properly and fast code this type of simple webpage containing a form using Python, Flask and WTF (What The Form) for Flask.

The idea of the project was to generate a HTML Button to link this blog to all my other articles written on a french web radio site. I wanted to do it as a website and also that the button was dynamic created to give the opportunity to all the others journalists of this radio to have their own button for their own blog (I'm a quite nice guy!). The result is not amazing but enough to learn some technical skills (if you're new to Flask).
# Main.py
import flask 
app = flask.Flask(__name__)

# Is called when you arrive on the site
@app.route('/', methods=['GET', 'POST'])
def home():
 # TBD 
 

if __name__ == '__main__':
 app.run()
So here, nothing special, we just configure Flask to make it run properly (without any config file or something, just it runs and that's enough for now).

Then, we will create our form class :
#forms.py
from flask_wtf import Form
from wtforms import TextField, BooleanField
from wtforms.validators import Required

# Here is a very simple class with defines the tree form elements that I'm using
class Form(Form):
    logic_name = TextField('Logical Name', validators = [Required()])
    is_displayed = BooleanField('Display a name', default = True)
    display_name = TextField('Display Name')

This class create the HTML elements and the python objects of the different fields of the form (installation of Flask WTF required).

Then, we need to create a HTML template which will be redenred by the python and which is containing the form (be careful, it's in a ugly language called French!) :

<!doctype html>
<html>
<head>
    <!-- Laurent MEYER, 2014, Apache License -->
    <!-- Done with Flask Framework -->
    <meta charset="UTF-8">
    <title>Créateur boutons RVL</title>
    <!--The url for is a method to generate a url for the browser to access to this resource-->
    <link href="{{ url_for('static', filename = 'style.css') }}" rel="stylesheet" type="text/css">
</head>

<body>

<div class="container">
    <div class="content">
        <h1>Créateur de chouettes boutons sans prétention pour RadioVL</h1>

        <p>N'ayant pas envie d'écrire d'article, je me suis demandé ce qui pourrait être marrant à faire sans prendre
            trop de temps et qui apporte un petit plus au média. Je me suis dit que ce serait chouette de créer un micro
            site qui créerait des boutons qui dirigeraient les visiteurs des blogs personels des journalistes de RadioVL
            vers leurs articles sur RadioVL, améliorant ainsi la visibilité de la radio (presque) sans travail.</p>

        <h2>Comment ça marche ?</h2>

        <p>1) Je suis pas graphiste donc le design du micro-site est fait à l'arrache mais c'est juste pour renouer avec
            des technologies que j'ai délaissées ces dernières années</p>

        <p>2) Remplissez le champ suivant avec votre nom d'auteur sur RadioVL que vous trouverez en allant sur un de vos
            articles et en cliquant sur votre nom ; voici une image pour vous donner un coup de main.</p>
        <center>
            <!--The url for is a method to generate a url for the browser to access to this resource-->
            <p><img src="{{ url_for('static', filename = 'example_name.png') }}" title="Description nom auteur"></p>
        </center>
        <p>Le nom à incrire est celui surligné en jaune.</p>

        <p>3) Si vous voulez que votre nom apparaisse sur le bouton selectionnez le oui et inscrivez le nom que vous
            voulez afficher, sinon cochez non.</p>

        <form action="" method="post" name="login">
            <p>
                Votre nom (pour wordpress) :<br>
                {{form.logic_name}}<br>
            </p>

            <p> Voulez-vous afficher un nom sur le bouton ?<br>
                {{form.is_displayed}}</p>

            <p>Le nom que vous désirez afficher :<br>
                {{form.display_name}}</p>

            <p><input type="submit" value="Créer mon bouton !"></p>
        </form>
        <!-- end .content --></p>
    </div>

    <!-- end .container --></div>
</body>
</html>

Ok, there are many useless content but the goal of this post, as I already said, is to learn with a real project. And there are few things to learn : first, to know how we can access to the static files (for example the CSS or the pictures). To do it, you need to understand that these are file cannot be accessed by your browser directly if they're located like this :

  
Why? Because it would be dangerous, don't you think ? If you got your database in one of these folders, it would mean that anyone could access to it (if he searches a bit), and potentially destroy it. That's why we're using this method which generates a url only for the file we wanna access/display. Second, when you're not writing HTML but Python variables or methods (that's also possible like the "url_for()"), the syntax is "{{(space)(content)(space)}}". Note that I didn't understand if we've to type the spaces but I find it clearer that's why I'm recommending it.

Ok, now, we'll try to render our template to have our first visual of the project. To do it, we've to add few lines in the Main.py:
# Main.py
import flask
from flask import render_template
app = flask.Flask(__name__)

# Is called when you arrive on the site
@app.route('/', methods=['GET', 'POST'])
def home():
 # Because it's dummy project, we disable the csrf verification for the form
 form = Form(csrf_enabled=False)
 return render_template('Main.html', form=form)
 

if __name__ == '__main__':
 app.run()
Here, we just create the form object (containing many fields) to pass it to the html renderer with the form = form which is meaning : "what's called 'form' on the html template is this object (the one we just created on the line above, also called form)".

If I'm not missing anything, we should have a result if you go on http://127.0.0.1:5000 (it won't work if you change it in the config file but if you did it, I'm not that sure that you need this tutorial at all! :) ).

Now, we'll experiment the really cool feature of WTF: I think that you've seen that I have a submit button at the end of my form and that if we click on it, yet, it will throw an exception. To solve it in one line, WTF is just great : just add this condition in your main and it'll work !

# Main.py

import flask
from forms import Form
from flask import render_template

app = flask.Flask(__name__)

# Is called when you arrive on the site
@app.route('/', methods=['GET', 'POST'])
def home():
 # Because it's dummy project, we disable the csrf verification for the form
 form = Form(csrf_enabled=False)
 return render_template('Main.html', form=form)
 # Here is what happens when you click on the submit button and all is ok
    if form.validate_on_submit():
     # do whatever you want, normally we do a redirection
 

if __name__ == '__main__':
 app.run()
Then, if all your fields are ok with all the conditions you've required, it'll be called and you'll be able to save the form, make a redirection or/and build a π calculator !
We'll just save the form (with a dirty method) and do a redirection to the page which will display a new page displaying the generated button. This time again it's quite simple :
# Main.py

import flask
from flask import render_template, redirect
import Model
from forms import Form

app = flask.Flask(__name__)

# Is called when you arrive on the site
@app.route('/', methods=['GET', 'POST'])
def home():
 # Because it's dummy project, we disable the csrf verification for the form
 form = Form(csrf_enabled=False)
 # Here is what happens when you click on the submit button and all is ok
    if form.validate_on_submit():
     # do whatever you want, normally we do a redirection
     Model.isDisplayed = form.is_displayed.data
        Model.name_logic = form.logic_name.data
        Model.nameDisplayed = " " + form.display_name.data
        # Redirect you to the result page
        return redirect('/createButton')
    # If the form is not validated (at the arrival on the website for example, it render the mainpage template)
    return render_template('Main.html', form=form)
 

if __name__ == '__main__':
 app.run()
As you're curious here is the model class (I do not think that it's a correct way but it has an advantage : it works all the time!):
# Model.py
__author__ = 'laurent'
name_logic = ""
isDisplayed = True
nameDisplayed = ""

Now we need the create button function which is, as you'll see, quite simple:
# Main.py

# Called if the form is validated (see above)
@app.route('/createButton', methods=['GET', 'POST'])
def create_button():
    # The boolean option which makes a very little difference between the two results
    if Model.isDisplayed == True:
        return render_template('Button.html', author=Model.name_logic, displayName=Model.nameDisplayed)
    else:
        return render_template('Button.html', author=Model.name_logic, displayName="-moi")
(you've to add this code to your main before the "if __name__ == '__main__': app.run()" in the Main.py).

And, finally, if you want to have the same as I've on the site, here is the code of the Button.html:
<html>
<head>
    <link href="{{ url_for('static', filename = 'style.css') }}" rel="stylesheet" type="text/css"></link>
</head>
<center>
    <div style="background-color: white; border: 1px solid #0d5ea3; width: 150px;">
<a href="http://www.radiovl.fr/author/%7B%7B%20author%20%7D%7D" style="color: black; text-decoration: none;"><img src="http://img11.hostingpics.net/pics/220182Buttonvierge.png" style="width: 150px;" />

        <div style="color: black; font-family: arial, helvetica, sans-serif; font-size: 12px; font-style: oblique; font-weight: bold; letter-spacing: 0pt; line-height: 1; text-align: center; text-shadow: 0px 1px 5px rgba(48,48,48,1); word-spacing: 1pt;">
            Suivez{{displayName}} sur RadioVL</div>
</a></div>
<div style="background-color: white;">
Code de ce bouton



        <div style="border:1px solid #0d5ea3;width:150px;background-color:#ffffff;"><a
            href="http://www.radiovl.fr/author/{{ author }}" style="text-decoration:none;color:#000;"><img
            src="http://img11.hostingpics.net/pics/220182Buttonvierge.png" style="width:150px"><p
            style="text-shadow:0px 1px 5px
            rgba(48,48,48,1);font-weight:bold;font-style:oblique;color:#000000;letter-spacing:0pt;word-spacing:1pt;font-size:12px;text-align:center;font-family:arial,
            helvetica, sans-serif;line-height:1;">Suivez{{displayName}} sur
            RadioVL</p></a></div>


    </div>
</center>
</html>

So, as you can see, my variables are using the {{(no space)(content)(no space)}} syntax and that seems to work so do what you prefer !

You can see the result of this code here.

I know that it was quite long and maybe quite annoying for the people who were looking for a quickfix but I hope you've learned a lot of things and that it will encourage you to use this great framework with this cool extension (which we did not use that much, it can manage much more complicated forms).

If I become 5 comments on this article (I need feedbacks to improve my English and/or the way I'm writing tutorials), I'll create a github of this simple project (and you'll have a project which works out of the box).

0 commentaires: