├── .gitignore ├── LICENSE ├── README.md ├── Resources ├── WTForms-field-types.csv └── WTForms-validators.csv ├── actors.py ├── actors_2.py ├── data.py ├── requirements.txt ├── static └── css │ └── main.css └── templates ├── 404.html ├── 500.html ├── actor.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac file types to be ignored 2 | .DS_Store 3 | .DS_Store? 4 | ._* 5 | .Spotlight-V100 6 | .Trashes 7 | ehthumbs.db 8 | Thumbs.db 9 | 10 | # Python executables 11 | *.pyc 12 | 13 | # folders to be ignored 14 | env/ 15 | __pycache__/ 16 | 17 | # Excel files 18 | *.xlsx 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 @macloo (Mindy McAdams) 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask Forms 2 | 3 | This is a very basic Flask application using two routes and an interactive form. It illustrates how to use [Flask-WTF](https://flask-wtf.readthedocs.io/en/stable/) and [Flask-Bootstrap](https://pythonhosted.org/Flask-Bootstrap/index.html) in a Flask app. 4 | 5 | The code is explained in [this slide deck](http://bit.ly/mm-flask-webforms-slides). 6 | 7 | A first version of the app is in **actors.py**. To run it, it is assumed that you: 8 | 9 | 1. Are using Python 3.x. 10 | 11 | 2. Have already installed Flask and its dependencies. 12 | 13 | 3. Have activated your virtualenv (if you are using one). 14 | 15 | Run the app by entering its folder in Terminal and typing: 16 | 17 | `$ python actors.py` 18 | 19 | Then open your web browser and type this in the address bar: 20 | 21 | `localhost:5000/` 22 | 23 | ## More functionality 24 | 25 | A second version of the app is in **actors_2.py**. 26 | 27 | Run the app by entering its folder in Terminal and typing: 28 | 29 | `$ python actors_2.py` 30 | 31 | Then open your web browser and type this in the address bar: 32 | 33 | `localhost:5000/` 34 | 35 | Note that if the Flask web server was already running before this, you'll need to stop it by pressing Control-C in Terminal before you run **actors_2.py**. 36 | 37 | ## Other Flask example files 38 | 39 | For a similar Flask app that does not use Flask-WTF or Flask-Bootstrap, see: [Flask Example](https://github.com/macloo/flask-example) 40 | 41 | For a complete basic Flask example with more templates, see: [Basic Flask App](https://github.com/macloo/basic-flask-app) 42 | -------------------------------------------------------------------------------- /Resources/WTForms-field-types.csv: -------------------------------------------------------------------------------- 1 | field_type,description 2 | StringField,"Text field (input type=""text"")" 3 | TextAreaField,Multiple-line text field (textarea) 4 | RadioField,"List of radio buttons (input type=""radio"")" 5 | BooleanField,"Checkbox with True and False values (input type=""checkbox"")" 6 | SelectField,Drop-down list of choices (select) 7 | SubmitField,"Form submission button (input type=""submit"")" 8 | PasswordField,"Password text field (input type=""password"")" 9 | HiddenField,"Hidden text field (input type=""hidden"")" 10 | DateField,Text field that accepts a datetime.date value in a given format 11 | DateTimeField,Text field that accepts a datetime.datetime value in a given format 12 | IntegerField,Text field that accepts an integer value 13 | DecimalField,Text field that accepts a decimal.Decimal value 14 | FloatField,Text field that accepts a floating-point value 15 | SelectMultipleField,Drop-down list of choices with multiple selection 16 | FileField,File upload field 17 | FormField,Embed a form as a field in a container form 18 | FieldList,List of fields of a given type -------------------------------------------------------------------------------- /Resources/WTForms-validators.csv: -------------------------------------------------------------------------------- 1 | validator,description Email,Validates an email address EqualTo,Compares the values of two fields; useful when requesting a password to be entered twice for confirmation IPAddress,Validates either an IPv4 or an IPv6 network address Length,Validates the length of the string entered NumberRange,"Validates that the value entered is within a given range, min to max" Optional,"Allows empty input on the field, skipping additional validators" Required,Validates that the field contains data Regexp,Validates the input against a regular expression URL,Validates a URL AnyOf,Validates that the input is one of a list of possible values NoneOf,Validates that the input is none of a list of possible values DataRequired,Checks that the data attribute on the field is a True value InputRequired,Validates that input was provided for this field MacAddress,Validates a MAC address UUID,Validates a UUID -------------------------------------------------------------------------------- /actors.py: -------------------------------------------------------------------------------- 1 | # using python 3 2 | from flask import Flask, render_template 3 | from flask_bootstrap import Bootstrap 4 | from flask_wtf import FlaskForm 5 | from wtforms import StringField, SubmitField 6 | from wtforms.validators import Required 7 | from data import ACTORS 8 | 9 | app = Flask(__name__) 10 | # Flask-WTF requires an enryption key - the string can be anything 11 | app.config['SECRET_KEY'] = 'some?bamboozle#string-foobar' 12 | # Flask-Bootstrap requires this line 13 | Bootstrap(app) 14 | # this turns file-serving to static, using Bootstrap files installed in env 15 | # instead of using a CDN 16 | app.config['BOOTSTRAP_SERVE_LOCAL'] = True 17 | 18 | # with Flask-WTF, each web form is represented by a class 19 | # "NameForm" can change; "(FlaskForm)" cannot 20 | # see the route for "/" and "index.html" to see how this is used 21 | class NameForm(FlaskForm): 22 | name = StringField('Which actor is your favorite?', validators=[Required()]) 23 | submit = SubmitField('Submit') 24 | 25 | # define functions to be used by the routes (just one here) 26 | 27 | # retrieve all the names from the dataset and put them into a list 28 | def get_names(source): 29 | names = [] 30 | for row in source: 31 | name = row["name"] 32 | names.append(name) 33 | return sorted(names) 34 | 35 | # all Flask routes below 36 | 37 | # two decorators using the same function 38 | @app.route('/', methods=['GET', 'POST']) 39 | @app.route('/index.html', methods=['GET', 'POST']) 40 | def index(): 41 | names = get_names(ACTORS) 42 | # you must tell the variable 'form' what you named the class, above 43 | # 'form' is the variable name used in this template: index.html 44 | form = NameForm() 45 | message = "" 46 | if form.validate_on_submit(): 47 | name = form.name.data 48 | if name in names: 49 | message = "Yay! " + name + "!" 50 | # empty the form field 51 | form.name.data = "" 52 | else: 53 | message = "That actor is not in our database." 54 | # notice that we don't need to pass name or names to the template 55 | return render_template('index.html', form=form, message=message) 56 | 57 | # keep this as is 58 | if __name__ == '__main__': 59 | app.run(debug=True) 60 | -------------------------------------------------------------------------------- /actors_2.py: -------------------------------------------------------------------------------- 1 | # using python 3 2 | from flask import Flask, render_template, redirect, url_for 3 | from flask_bootstrap import Bootstrap 4 | from flask_wtf import FlaskForm 5 | from wtforms import StringField, SubmitField 6 | from wtforms.validators import Required 7 | from data import ACTORS 8 | 9 | app = Flask(__name__) 10 | # Flask-WTF requires an enryption key - the string can be anything 11 | app.config['SECRET_KEY'] = 'some?bamboozle#string-foobar' 12 | # Flask-Bootstrap requires this line 13 | Bootstrap(app) 14 | # this turns file-serving to static, using Bootstrap files installed in env 15 | # instead of using a CDN 16 | app.config['BOOTSTRAP_SERVE_LOCAL'] = True 17 | 18 | # with Flask-WTF, each web form is represented by a class 19 | # "NameForm" can change; "(FlaskForm)" cannot 20 | # see the route for "/" and "index.html" to see how this is used 21 | class NameForm(FlaskForm): 22 | name = StringField('Which actor is your favorite?', validators=[Required()]) 23 | submit = SubmitField('Submit') 24 | 25 | # define functions to be used by the routes 26 | 27 | # retrieve all the names from the dataset and put them into a list 28 | def get_names(source): 29 | names = [] 30 | for row in source: 31 | name = row["name"] 32 | names.append(name) 33 | return sorted(names) 34 | 35 | # find the row that matches the id in the URL, retrieve name and photo 36 | def get_actor(source, id): 37 | for row in source: 38 | if id == str( row["id"] ): 39 | name = row["name"] 40 | photo = row["photo"] 41 | # change number to string 42 | id = str(id) 43 | # return these if id is valid 44 | return id, name, photo 45 | # return these if id is not valid - not a great solution, but simple 46 | return "Unknown", "Unknown", "" 47 | 48 | # find the row that matches the name in the form and retrieve matching id 49 | def get_id(source, name): 50 | for row in source: 51 | if name == row["name"]: 52 | id = row["id"] 53 | # change number to string 54 | id = str(id) 55 | # return id if name is valid 56 | return id 57 | # return these if id is not valid - not a great solution, but simple 58 | return "Unknown" 59 | 60 | # all Flask routes below 61 | 62 | # two decorators using the same function 63 | @app.route('/', methods=['GET', 'POST']) 64 | @app.route('/index.html', methods=['GET', 'POST']) 65 | def index(): 66 | names = get_names(ACTORS) 67 | # you must tell the variable 'form' what you named the class, above 68 | # 'form' is the variable name used in this template: index.html 69 | form = NameForm() 70 | message = "" 71 | if form.validate_on_submit(): 72 | name = form.name.data 73 | if name in names: 74 | # empty the form field 75 | form.name.data = "" 76 | id = get_id(ACTORS, name) 77 | # redirect the browser to another route and template 78 | return redirect( url_for('actor', id=id) ) 79 | else: 80 | message = "That actor is not in our database." 81 | return render_template('index.html', names=names, form=form, message=message) 82 | 83 | 84 | @app.route('/actor/') 85 | def actor(id): 86 | # run function to get actor data based on the id in the path 87 | id, name, photo = get_actor(ACTORS, id) 88 | if name == "Unknown": 89 | # redirect the browser to the error template 90 | return render_template('404.html'), 404 91 | else: 92 | # pass all the data for the selected actor to the template 93 | return render_template('actor.html', id=id, name=name, photo=photo) 94 | 95 | 96 | # routes to handle errors - they have templates too 97 | 98 | @app.errorhandler(404) 99 | def page_not_found(e): 100 | return render_template('404.html'), 404 101 | 102 | @app.errorhandler(500) 103 | def internal_server_error(e): 104 | return render_template('500.html'), 500 105 | 106 | 107 | # keep this as is 108 | if __name__ == '__main__': 109 | app.run(debug=True) 110 | -------------------------------------------------------------------------------- /data.py: -------------------------------------------------------------------------------- 1 | ACTORS = [ 2 | {"id":26073614,"name":"Al Pacino","photo":"https://placebear.com/342/479"}, 3 | {"id":77394988,"name":"Anthony Hopkins","photo":"https://placebear.com/305/469"}, 4 | {"id":44646271,"name":"Audrey Hepburn","photo":"https://placebear.com/390/442"}, 5 | {"id":85626345,"name":"Barbara Stanwyck","photo":"https://placebear.com/399/411"}, 6 | {"id":57946467,"name":"Barbra Streisand","photo":"https://placebear.com/328/490"}, 7 | {"id":44333164,"name":"Ben Kingsley","photo":"https://placebear.com/399/442"}, 8 | {"id":91171557,"name":"Bette Davis","photo":"https://placebear.com/318/496"}, 9 | {"id":25163439,"name":"Brad Pitt","photo":"https://placebear.com/359/430"}, 10 | {"id":59236719,"name":"Cate Blanchett","photo":"https://placebear.com/346/407"}, 11 | {"id":35491900,"name":"Christian Bale","photo":"https://placebear.com/363/440"}, 12 | {"id":48725048,"name":"Christoph Waltz","photo":"https://placebear.com/356/445"}, 13 | {"id":36858988,"name":"Christopher Walken","photo":"https://placebear.com/328/448"}, 14 | {"id":77052052,"name":"Clint Eastwood","photo":"https://placebear.com/311/428"}, 15 | {"id":74729990,"name":"Daniel Day-Lewis","photo":"https://placebear.com/393/480"}, 16 | {"id":75495971,"name":"Denzel Washington","photo":"https://placebear.com/392/471"}, 17 | {"id":49842505,"name":"Diane Keaton","photo":"https://placebear.com/310/462"}, 18 | {"id":16533679,"name":"Dustin Hoffman","photo":"https://placebear.com/306/425"}, 19 | {"id":77778079,"name":"Ed Harris","photo":"https://placebear.com/365/430"}, 20 | {"id":62340777,"name":"Edward Norton","photo":"https://placebear.com/356/486"}, 21 | {"id":45282572,"name":"Elizabeth Taylor","photo":"https://placebear.com/338/440"}, 22 | {"id":98253023,"name":"Ellen Burstyn","photo":"https://placebear.com/313/452"}, 23 | {"id":14389118,"name":"Emma Thompson","photo":"https://placebear.com/314/445"}, 24 | {"id":85245270,"name":"Faye Dunaway","photo":"https://placebear.com/352/495"}, 25 | {"id":77497258,"name":"Frances McDormand","photo":"https://placebear.com/368/404"}, 26 | {"id":69175505,"name":"Gary Oldman","photo":"https://placebear.com/380/468"}, 27 | {"id":52103198,"name":"Gene Hackman","photo":"https://placebear.com/326/464"}, 28 | {"id":53511355,"name":"George Clooney","photo":"https://placebear.com/350/405"}, 29 | {"id":46135141,"name":"Glenn Close","photo":"https://placebear.com/318/446"}, 30 | {"id":67466115,"name":"Grace Kelly","photo":"https://placebear.com/396/422"}, 31 | {"id":32111872,"name":"Greer Garson","photo":"https://placebear.com/331/464"}, 32 | {"id":38069638,"name":"Greta Garbo","photo":"https://placebear.com/344/467"}, 33 | {"id":30546602,"name":"Harrison Ford","photo":"https://placebear.com/324/471"}, 34 | {"id":78852790,"name":"Harvey Keitel","photo":"https://placebear.com/302/498"}, 35 | {"id":75916952,"name":"Heath Ledger","photo":"https://placebear.com/354/420"}, 36 | {"id":44647231,"name":"Helen Mirren","photo":"https://placebear.com/390/401"}, 37 | {"id":98319284,"name":"Hilary Swank","photo":"https://placebear.com/334/428"}, 38 | {"id":73307095,"name":"Holly Hunter","photo":"https://placebear.com/388/439"}, 39 | {"id":65927229,"name":"Ian McKellen","photo":"https://placebear.com/359/447"}, 40 | {"id":84415199,"name":"Ingrid Bergman","photo":"https://placebear.com/341/487"}, 41 | {"id":28676619,"name":"Jack Nicholson","photo":"https://placebear.com/335/461"}, 42 | {"id":66339602,"name":"Jane Fonda","photo":"https://placebear.com/323/471"}, 43 | {"id":22447589,"name":"Jane Wyman","photo":"https://placebear.com/373/469"}, 44 | {"id":52907687,"name":"Javier Bardem","photo":"https://placebear.com/366/421"}, 45 | {"id":35506267,"name":"Jeff Bridges","photo":"https://placebear.com/388/422"}, 46 | {"id":16807306,"name":"Jennifer Jones","photo":"https://placebear.com/333/498"}, 47 | {"id":63521043,"name":"Jessica Lange","photo":"https://placebear.com/309/413"}, 48 | {"id":91132400,"name":"Joan Crawford","photo":"https://placebear.com/322/437"}, 49 | {"id":74127064,"name":"Joan Fontaine","photo":"https://placebear.com/329/458"}, 50 | {"id":30283700,"name":"Joaquin Phoenix","photo":"https://placebear.com/316/452"}, 51 | {"id":40834926,"name":"Jodie Foster","photo":"https://placebear.com/307/439"}, 52 | {"id":59676726,"name":"Joe Pesci","photo":"https://placebear.com/388/434"}, 53 | {"id":18959030,"name":"Johnny Depp","photo":"https://placebear.com/337/422"}, 54 | {"id":19220801,"name":"Judi Dench","photo":"https://placebear.com/397/480"}, 55 | {"id":41880845,"name":"Judy Garland","photo":"https://placebear.com/352/472"}, 56 | {"id":38744009,"name":"Julia Roberts","photo":"https://placebear.com/331/441"}, 57 | {"id":46032709,"name":"Julianne Moore","photo":"https://placebear.com/331/402"}, 58 | {"id":63172387,"name":"Julie Andrews","photo":"https://placebear.com/320/416"}, 59 | {"id":28367735,"name":"Kate Winslet","photo":"https://placebear.com/329/432"}, 60 | {"id":31109338,"name":"Katharine Hepburn","photo":"https://placebear.com/366/475"}, 61 | {"id":81511778,"name":"Kathy Bates","photo":"https://placebear.com/387/440"}, 62 | {"id":58030620,"name":"Kevin Spacey","photo":"https://placebear.com/321/425"}, 63 | {"id":96645151,"name":"Leonardo DiCaprio","photo":"https://placebear.com/347/495"}, 64 | {"id":57846776,"name":"Luise Rainer","photo":"https://placebear.com/311/448"}, 65 | {"id":26406570,"name":"Marilyn Monroe","photo":"https://placebear.com/305/441"}, 66 | {"id":17017025,"name":"Matt Damon","photo":"https://placebear.com/306/421"}, 67 | {"id":84422863,"name":"Mel Gibson","photo":"https://placebear.com/301/484"}, 68 | {"id":26357464,"name":"Meryl Streep","photo":"https://placebear.com/343/401"}, 69 | {"id":87469606,"name":"Michael Caine","photo":"https://placebear.com/309/481"}, 70 | {"id":50738795,"name":"Michael Douglas","photo":"https://placebear.com/393/496"}, 71 | {"id":61071401,"name":"Morgan Freeman","photo":"https://placebear.com/348/448"}, 72 | {"id":28005840,"name":"Natalie Portman","photo":"https://placebear.com/323/448"}, 73 | {"id":50786809,"name":"Natalie Wood","photo":"https://placebear.com/386/458"}, 74 | {"id":40122578,"name":"Nicolas Cage","photo":"https://placebear.com/319/481"}, 75 | {"id":75794861,"name":"Nicole Kidman","photo":"https://placebear.com/375/443"}, 76 | {"id":81504488,"name":"Olivia de Havilland","photo":"https://placebear.com/388/450"}, 77 | {"id":43406362,"name":"Paul Newman","photo":"https://placebear.com/335/456"}, 78 | {"id":44428849,"name":"Peter O'Toole","photo":"https://placebear.com/313/467"}, 79 | {"id":60338818,"name":"Philip Seymour Hoffman","photo":"https://placebear.com/389/458"}, 80 | {"id":56249953,"name":"Ralph Fiennes","photo":"https://placebear.com/301/467"}, 81 | {"id":33056256,"name":"Robert De Niro","photo":"https://placebear.com/339/439"}, 82 | {"id":36150440,"name":"Robert Downey Jr.","photo":"https://placebear.com/393/409"}, 83 | {"id":55284258,"name":"Robert Duvall","photo":"https://placebear.com/310/472"}, 84 | {"id":49032807,"name":"Robert Redford","photo":"https://placebear.com/326/443"}, 85 | {"id":14332961,"name":"Russell Crowe","photo":"https://placebear.com/336/436"}, 86 | {"id":47048008,"name":"Sally Field","photo":"https://placebear.com/301/452"}, 87 | {"id":64664156,"name":"Samuel L. Jackson","photo":"https://placebear.com/364/491"}, 88 | {"id":63841892,"name":"Sean Penn","photo":"https://placebear.com/344/496"}, 89 | {"id":80868139,"name":"Shirley MacLaine","photo":"https://placebear.com/389/475"}, 90 | {"id":22571419,"name":"Sigourney Weaver","photo":"https://placebear.com/304/464"}, 91 | {"id":23127852,"name":"Sissy Spacek","photo":"https://placebear.com/318/427"}, 92 | {"id":91913599,"name":"Sophia Loren","photo":"https://placebear.com/360/476"}, 93 | {"id":34324158,"name":"Steve Buscemi","photo":"https://placebear.com/367/467"}, 94 | {"id":54027019,"name":"Susan Hayward","photo":"https://placebear.com/376/476"}, 95 | {"id":49932101,"name":"Susan Sarandon","photo":"https://placebear.com/335/480"}, 96 | {"id":28040377,"name":"Tom Cruise","photo":"https://placebear.com/305/482"}, 97 | {"id":34061331,"name":"Tom Hanks","photo":"https://placebear.com/344/424"}, 98 | {"id":24724551,"name":"Tommy Lee Jones","photo":"https://placebear.com/315/465"}, 99 | {"id":27085641,"name":"Viola Davis","photo":"https://placebear.com/368/481"}, 100 | {"id":91326710,"name":"Vivien Leigh","photo":"https://placebear.com/388/435"}, 101 | {"id":68598976,"name":"Willem Dafoe","photo":"https://placebear.com/347/447"} 102 | ] 103 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.3 2 | click==6.7 3 | dominate==2.3.1 4 | Flask==0.12 5 | Flask-Bootstrap==3.3.7.1 6 | Flask-WTF==0.14.2 7 | itsdangerous==0.24 8 | Jinja2==2.9.5 9 | MarkupSafe==1.0 10 | packaging==16.8 11 | pyparsing==2.2.0 12 | six==1.10.0 13 | visitor==0.1.3 14 | Werkzeug==0.11.15 15 | WTForms==2.1 16 | -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | /* to override Bootstrap styles for some things */ 2 | 3 | .space-above { 4 | margin-top: 20px; 5 | padding-top: 20px; 6 | } 7 | 8 | .blocker { 9 | display: block; 10 | } 11 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'bootstrap/base.html' %} 2 | 3 | {% block title %} 4 | 404 Error - Page Not Found 5 | {% endblock %} 6 | 7 | 8 | {% block content %} 9 | 10 |
11 |
12 |
13 | 14 |

Page not found (Error 404)

15 | 16 |

Go to search page

17 | 18 |
19 |
20 |
21 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'bootstrap/base.html' %} 2 | 3 | {% block title %} 4 | 500 Error - Internal Server Error 5 | {% endblock %} 6 | 7 | 8 | {% block content %} 9 | 10 |
11 |
12 |
13 | 14 |

Internal server error (500)

15 | 16 |

Go to search page

17 | 18 |
19 |
20 |
21 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /templates/actor.html: -------------------------------------------------------------------------------- 1 | {% extends 'bootstrap/base.html' %} 2 | 3 | {% block styles %} 4 | {{ super() }} 5 | 6 | {% endblock %} 7 | 8 | 9 | {% block title %} 10 | Selected Actor: {{ name }} 11 | {% endblock %} 12 | 13 | 14 | {% block content %} 15 | 16 |
17 |
18 |
19 | 20 | 21 | 22 |

{{ name }}

23 | 24 |

Return to search page

25 | 26 | 30 | 31 |
32 |
33 | 34 | Photo: {{ name }} 35 | 36 |

Above: {{ name }}

37 | 38 |
39 |
40 |
41 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'bootstrap/base.html' %} 2 | {% import "bootstrap/wtf.html" as wtf %} 3 | 4 | {% block styles %} 5 | {{ super() }} 6 | 7 | {% endblock %} 8 | 9 | 10 | {% block title %} 11 | Best Movie Actors 12 | {% endblock %} 13 | 14 | 15 | {% block content %} 16 | 17 |
18 |
19 |
20 | 21 |

Welcome to the best movie actors Flask example!

22 | 23 |

This is the index page for an example Flask app using Bootstrap and WTForms.

24 | 25 | {{ wtf.quick_form(form) }} 26 | 27 |

{{ message }}

28 | 29 |
30 |
31 |
32 | 33 | 46 | 47 | {% endblock %} 48 | --------------------------------------------------------------------------------