Flask in Python is a micro web framework used to run major websites including Pinterest, Twilio, and LinkedIn. This topic explains and demonstrates the variety of features Flask offers for both front and back end web development.
Flask in Python: Files and Templates
Instead of typing our HTML markup into the return statements, we can use the render_template() function:
from flask import Flask
from flask import render_template
app = Flask(name)
@app.route("/about")
def about():
return render_template("about-us.html")
if name == "main":
app.run(host="0.0.0.0", port=80, debug=True)
This will use our template file about-us.html. To ensure our application can find this file we must organize our directory in the following format:
application.py
/templates
about-us.html
login-form.html /static
/styles
about-style.css
login-style.css /scripts
about-script.js
login-script.js
Most importantly, references to these files in the HTML must look like this:
which will direct the application to look for about-style.css in the styles folder under the static folder. The same format of path applies to all references to images, styles, scripts, or files.
Flask in Python: The basics
The following example is an example of a basic server:
Imports the Flask class from flask import Flask
Creates an app and checks if it's the main or imported app = Flask(name)
Specifies what URL triggers hello_world()
@app.route('/')
The function run on the index route def hello_world():
Returns the text to be displayed
return "Hello World!"
If this script isn't an import if name == "main":
Run the app until stopped app.run()
Running this script (with all the right dependencies installed) should start up a local server. The host is 127.0.0.1 commonly known as localhost. This server by default runs on port 5000. To access your webserver, open a web browser and enter the URL localhost:5000 or 127.0.0.1:5000 (no difference). Currently, only your computer can access the webserver.
app.run() has three parameters, host, port, and debug. The host is by default 127.0.0.1, but setting this to
0.0.0.0 will make your web server accessible from any device on your network using your private IP address in the URL. the port is by default 5000 but if the parameter is set to port 80, users will not need to specify a port number as browsers use port 80 by default. As for the debug option, during the development process (never in production) it helps to set this parameter to True, as your server will restart when changes made to your Flask project.
if name == "main":
app.run(host="0.0.0.0", port=80, debug=True)
Routing URLs
With Flask, URL routing is traditionally done using decorators. These decorators can be used for static routing, as well as routing URLs with parameters. For the following example, imagine this Flask script is running the website www.example.com.
@app.route("/")
def index():
return "You went to www.example.com"
@app.route("/about")
def about():
return "You went to www.example.com/about"
@app.route("/users/guido-van-rossum")
return "You went to www.example.com/guido-van-rossum"
With that last route, you can see that given a URL with /users/ and the profile name, we could return a profile. Since it would be horribly inefficient and messy to include a @app.route() for every user, Flask offers to take parameters from the URL:
@app.route("/users/") def profile(username): return "Welcome to the profile of " + username cities = ["OMAHA", "MELBOURNE", "NEPAL", "STUTTGART", "LIMA", "CAIRO", "SHANGHAI"] @app.route("/stores/locations/") def storefronts(city): if city in cities: return "Yes! We are located in " + city else: return "No. We are not located in " + city
HTTP Methods
The two most common HTTP methods are GET and POST. Flask can run different code from the same URL dependent on the HTTP method used. For example, in a web service with accounts, it is most convenient to route the sign in page and the sign in process through the same URL. A GET request, the same that is made when you open a URL in your browser should show the login form, while a POST request (carrying login data) should be processed separately. A route is also created to handle the DELETE and PUT HTTP method.
@app.route("/login", methods=["GET"])
def login_form():
return "This is the login form"
@app.route("/login", methods=["POST"])
def login_auth():
return "Processing your data"
@app.route("/login", methods=["DELETE", "PUT"])
def deny():
return "This method is not allowed"
To simplify the code a bit, we can import the request package from flask.
from flask import request
@app.route("/login", methods=["GET", "POST", "DELETE", "PUT"])
def login():
if request.method == "DELETE" or request.method == "PUT":
return "This method is not allowed"
elif request.method == "GET":
return "This is the login forum"
elif request.method == "POST":
return "Processing your data"
To retrieve data from the POST request, we must use the request package:
from flask import request
@app.route("/login", methods=["GET", "POST", "DELETE", "PUT"])
def login():
if request.method == "DELETE" or request.method == "PUT":
return "This method is not allowed"
elif request.method == "GET":
return "This is the login forum"
elif request.method == "POST":
return "Username was " + request.form["username"] + " and password was " + request.form["password"]
Jinja Templating
Similar to Meteor.js, Flask integrates well with front end templating services. Flask uses by default Jinja Templating.
Templates allow small snippets of code to be used in the HTML file such as conditionals or loops.
When we render a template, any parameters beyond the template file name are passed into the HTML templating service. The following route will pass the username and joined date (from a function somewhere else) into the HTML.
@app.route("/users/) def profile(username): joinedDate = get_joined_date(username) # This function's code is irrelevant awards = get_awards(username) # This function's code is irrelevant
The joinDate is a string and awards is an array of strings
return render_template("profile.html", username=username, joinDate=joinDate, awards=awards)
When this template is rendered, it can use the variables passed to it from the render_template() function. Here are the contents of profile.html:
if username
Profile of {{ username }}
else
No User Found
endif
{% if username %}
{{ username }} joined on the date {{ date }}
{% if len(awards) > 0 %}
{{ username }} has the following awards:
{{award}}
{% else %}
{{ username }} has no awards
{% endif %}
{% else %}
No user was found under that username
{% endif %}
{# This is a comment and doesn't affect the output #}
The following delimiters are used for different interpretations:
{% … %} denotes a statement
{{ … }} denotes an expression where a template is outputted
{# … #} denotes a comment (not included in template output)
{# … ## implies the rest of the line should be interpreted as a statement
Flask in Python The Request Object
The request object provides information on the request that was made to the route. To utilize this object, it must be imported from the flask module:
from flask import request
URL Parameters
In previous examples request.method and request.form were used, however we can also use the request.args property to retrieve a dictionary of the keys/values in the URL parameters.
@app.route(“/api/users/”)
def user_api(username):
try:
token = request.args.get(“key”)
if key == “pA55w0Rd”:
GoalKicker.com – Python® Notes for Professionals 549
if isUser(username): # The code of this method is irrelevant
joined = joinDate(username) # The code of this method is irrelevant return "User " + username + " joined on " + joined
else:
return "User not found"
else:
return "Incorrect key"
If there is no key parameter except KeyError:
return "No key provided"
To correctly authenticate in this context, the following URL would be needed (replacing the username with any username:
www.example.com/api/users/guido-van-rossum?key=pa55w0Rd
File Uploads
If a file upload was part of the submitted form in a POST request, the files can be handled using the request object:
@app.route("/upload", methods=["POST"])
def upload_file():
f = request.files["wordlist-upload"]
f.save("/var/www/uploads/" + f.filename) # Store with the original filename
Cookies
The request may also include cookies in a dictionary similar to the URL parameters.
@app.route("/home")
def home():
try:
username = request.cookies.get("username")
return "Your stored username is " + username
except KeyError:
return "No username cookies was found")