Outline
- -1. Who am I and what am I doing here?
Outline
- 1. Python
- Installation
- (Very) short syntax overview
- Package management
- Virtual environments
- Packages and modules
Outline
- 2. Flask
- Setup
- Routing
- Extensions
- Templates
- Persistence
-1. Who am I and what am I doing here?
I'm Simon
- Began using Python by writing small scripts on my Linux systems
- Used it even more while working in the field of computer vision at the university
- One day I needed a REST backend and naturally began writing it in Python
- And now I'm here to tell you about it!
Python Web Development
- Python mostly used for server side programming
- Not necessarily, but usually done using web frameworks
- Allow you to focus on application logic and not worry about low-level details
- And since you can convert *anything* to JavaScript: Transpilers available to generate JS from Python for client side programming
Web Frameworks
- Full Stack: Has everything you (may not even) need
- Application server, persistence API, template engine, authentication module, AJAX support, etc.
- Examples: Django, TurboGears, web2py
- Micro: Provides a basic setup to get started
- Application server, (template engine)
- Additional requirements are (eventually) brought in via plug-ins
- Examples: Flask, Bottle, Pyramid
Python
- Object oriented
- Strong dynamic typing
- Memory management
- Module and package system
- Feature rich standard library
- Provides a REPL
Python
- No brackets, semicolons, etc.
- Code is structured using indentation
- Standard container classes: Lists, Tuples, Dictionaries (Maps), Sets
- Iterators
- List comprehensions
Python - Styleguide
- Indent with 4 spaces
- Class names should be CapWordFormatted
- Function names should be lowercase, words separated by underscores
- Same holds for variable names
- Complete styleguide: PEP 8
Python - Installation
- Linux and macOS should come with a pre-installed Python distribution. Since these distributions are rather old, we can install a newer one
- Linux: Ask you local package manager (e.g. sudo apt-get install python3 or sudo pacman -S python)
- macOS: Get it from the brewery (brew install python3)
- Python provides a setup for Windows systems
- Make sure to check the option to add Python to your path at the bottom of the first setup screen and it should be availabe from the command line right away
Python Examples - Hello world
# hello_1.py
print("Hello world!")
$ python hello_1.py
Python Examples - Comments
# I'm a single line comment
# We arrrr
# pirates! (And a multi line comment!)
'''
So arrrr we!
'''
Python Examples - Iterating
list_example = [1, 2, 3, 4, 5, 6]
for element in list_example:
print(element)
for element in reversed(list_example):
print(element)
Python Examples - Classes
class A:
def __init__(self): # Constructor
self.variable = 42
self._protected_variable = 23
self.__private_variable = 5
def say_hi(self):
print("Hi Consolis!")
Python Examples - Exception handling
try:
some_method()
except some_exception as e:
print(e)
# raise
Python - Package management
- pip - Pip Installs Python
- Common package management tool
- Packages are listed in the Python Package Index
- As of today, 111187 third-party packages are available
Python - Package management
pip install ${package}
pip uninstall ${package}
pip list
pip freeze > requirements.txt
pip install -r requirements.txt
Python - Virtual environments
- Packages are installed in a per-user or system wide repository
- Many different versions clutter the system
- Virtual environments avoid this by creating local copies of a Python environment
pip install virtualenv
Python - Activating virtual environment
virtualenv venv
source venv/bin/activate
(venv)${PS1}
Python - Modules
What's a module in Python?
- Every single source file in Python represents a module
- It's possible to keep all your code in a single module
- It's better to separate it into multiple modules
- Modules can be imported
- Once a module gets imported, all of its statements are executed until EOF
- What's left on global scope after this initial execution is the modules content
Python - Module example
# consol.py
def say_hi():
print("Hi ConSolis!")
# main.py
import consol
consol.say_hi()
Python - Packages
And what's a package?
- A package is a collection of modules within a folder
- This folder needs an '__init__.py' file
- (Kind of like a package constructor)
- Imports can be relative (recommended for flexibility)
Flask
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions
Flask
- Werkzeug:
- Werkzeug is a WSGI utility library for Python under BSD license
- Jinja 2:
- Jinja 2 is a full featured template engine for Python with built-in unicode support
- Good intentions:
- Armin Ronacher is a great open source contributor and wrote all of the above and more
Flask - Project structure
- flask/ <- Project folder
|- shoutbox/ <- Application subfolder
|- __init__.py <- shoutbox package
|- venv/ <- Virtual environment
|- run.py <- Script to run application
Flask - Setup
git clone https://github.com/s1hofmann/shoutbox.git
git checkout tags/flask_init
Flask - Setup
- Inside the subfolder 'flask', create a new virtual environment called venv
virtualenv venv
source venv/bin/activate or venv\Scripts\activate
pip install flask
Flask - Setup
- The basic project structure is now set up
- The run.py script is used to run the server (later used for FastCGI)
- Everything related to the actual web application goes inside shoutbox/
- Packages and interpreter are located in venv/
Flask - Hello world
- Edit the file shoutbox_app.py inside the shoutbox folder
# shoutbox/shoutbox_app.py
from flask import Flask
__all__ = ['app'] # Added for imports
app = Flask(__name__)
@app.route('/')
def index():
return "Hello world!"
Flask - Hello world
- Next, import all symbols from shoutbox.py to the package
# shoutbox/__init__.py
from .shoutbox_app import *
Flask - Hello world
- Finally, make the shoutbox application available in run.py
# run.py
#!venv/bin/python
import shoutbox
if __name__ == "__main__":
shoutbox.app.run()
Flask - Hello world
- Now if we start our application
venv/bin/python run.py
- We should see our server listen on port 5000:
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Flask - Request routing
- Routing in Flask is configured using decorators
- Two things are required: A route decorator and its corresponding view function
@app.route('/') # Route decorator
def index(): # View function
return "Hello world!"
Flask - Request routing
- Routing supports path variables
- Path variables are specified in angle brackets with an optional type
- The view function receives an argument named after the path variable
@app.route('/hello/‹string:username›') # Route decorator
def say_hi(username): # View function
pass
Flask - Routing exercise
- Complete the '/hello/' route to return a customized string
Flask - Request hooks
- Request hooks can carry out actions before or after requests are routed
- (Similar to servlet filters)
- Flask provides four different hooks
Hook |
Description |
@before_first_request
| Runs before the very first request
|
@before_request
| Runs before every request
|
@after_request
| Runs after every successful request
|
@teardown_request
| Runs after every request
|
Flask - Responses
- Flasks automatically wraps return values into valid HTTP responses
- Return value, status code and headers can be represented as tuple
- If no status code is provided, 200 Ok is returned
@app.index('/')
def index():
return ("Hello world!", 204)
Flask - Templates
- We don't actually want to put presentation logic into our controller
- Flasks comes with a template engine called jinja2 to handle this problem
- Templates are rendered by calling the render_template("$template.html") method
- HTML with additional template functions
Flask - Template variables
- render_template takes variable mappings for variables which will be substituted in the template
- render_template("$template.html", template_var=var) substitutes template_var with var
{{ template_var }}
Flask - Template blocks
- Template blocks are defined parts of a template which can be overidden
{% block $name %}
{% endblock %}
Flask - Template inheritance
- Templates can inherit from a super template and override blocks
{% extends 'super_template.html' %}
Flask - Template inheritance
- Similar to inheritance in Python, super templates have to be initialized
- Each blocks requires a super call, otherwise the super templates content will be lost
{% block derived %}
{{ super() }}
{% endblock %}
Flask - Template includes
- Template can include other templates to assemble complex elements
{% include 'other_template.html' %}
Flask - Template control structures
{% if $condition %}
statement
{% else %}
other_statement
{% endif %}
Flask - Template loops
{% for element in elements %}
statement
{% endfor %}
Flask - Templates exercise
- Create template templates/hello.html which renders a personal greeting to the user
- Update the /hello/ route to render your template
@app.route('/hello/‹string:username›')
def say_hi(username):
return render_template(...)
Shortcut!
- Now that we've covered the basics on routes and templates, let's move a bit ahead
git checkout tags/webforms_init
pip install -r requirements.txt
What's next?
- Let's implement a little shout box where users can enter their name and let the world know what grinds their gears!
- What's missing?
- A form to enter data
- A way to persist data
- A way to render entered data
- Filtering shouts per user
Flask - WebForms
- Flask-WTF provides enhanced features to handle web forms
- It supports you in generating HTML code for web forms
- Provides CSRF protection out of the box
pip install flask-wtf
Flask - WebForms
- Forms hold objects derived from Form class
- Standard HTML fields supported: StringField, DateField, IntegerField, SelectField, etc.
- Validators supported: Required, Optional, Length, EqualTo, etc.
- Form content will be available via variables
Flask - WebForms
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class MyForm(FlaskForm):
name = StringField('name', validators=[DataRequired()])
submit = SubmitField('submit')
Flask - WebForms exercise
- Create a new file shoutbox/forms.py
- Using the previous sample, build up a ShoutForm
- Provide a StringField for the name, a TextAreaField for the shout and a SubmitField to submit
Intermission - Flask config
- When trying to render the ShoutForm for the first time, a KeyError should be raised
- What's missing is a secret key flask-wtf tries to use to prevent CSRF
- Flask provides a global secret key, which can be set in the app config map
# shoutbox/shoutbox_app.py
app.config['SECRET_KEY'] = "Don't tell anyone!"
Flask - WebForms
- After setting the secret key we still encounter an error
- Per default, Flask only allows "GET" requests
- Additional methods are specified on the route decorator
@app.route('/shout/', methods=["GET", "POST"])
Flask - Persistence
- Now that we got our form set up, we need a way to persist inputs
- Flask provides a plugin to work with SQLAlchemy, a powerful ORM
git checkout tags/database_init
pip install flask-sqlalchemy
Flask - Persistence
- The repository contains an SQLite3 database with the following layout
name |
type |
id
| INTEGER
|
username
| TEXT
|
text
| TEXT
|
Flask - Persistence
- The Flask config requires the path to the database
- This path is stored in the config under the key "SQLALCHEMY_DATABASE_URI"
- Checkout the tag and configure the database path
Flask - Persistence
- The SQLAlchemy engine is initialized by passing the application object to SQLAlchemy
# shoutbox/shoutbox_app.py
db = SQLAlchemy(app)
- The newly created SQLAlchemy object contains several high level ORM objects like Model, Column or types
Flask - Defining persistence models
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Text)
email = db.Column(db.Text)
def __init__(self, username, email):
self.username = username
self.email = email
Flask - Defining persistence models
- Create a new file "shoutbox/models.py"
- In this file, import the db object from shoutbox_app
- Using the db object, create your Shout database model
Flask - Cyclic imports
- We need to make the newly defined model available in our controller
- We'll have to import the models module in shoutbox_app
- For this to work, the module has to be imported after the db object has been initialized
Summary
- Flask is a very flexible and extensible framework
- MVC pattern with SQLAlchemy, Jinja2 and Flask
- Lots of additional extensions
- Login
- OAuth
- Websockets
- and more...
- Check the Flask Extension Registry
Thank's for your attention!
Appendix A - External configuration
- Keeping configuration elements inside the code is not that great
- Flask provides a way to configure the application object using a config class
class Config:
SECRET_KEY = 'hard to guess'
DEBUG = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///shoutbox.db'
app.config.from_object($config_object)
Appendix B - Logging
- Logging is essential in web apps
- The application object provides a default logger
app.logger.debug('A value for debugging')
app.logger.error('An error occurred')
- app.logger is a standard logging Logger
- Allows easy modification by adding custom handlers
Appendix C - Custom error pages
- Flask per default renders standard black-and-white error pages
- Decorators allow to register custoom error pages
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
- Remember to set the correct return status in render_template
Appendix D - Crash reporting
- Uncaught exceptions are very bad
- Logging helps to trace them
- Sentry.io provides crash reporting which integrates seamlessly with Flask
from raven.contrib.flask import Sentry
sentry = Sentry()
sentry.init_app(app, dsn=$sentry_key)
- That's all it takes for detailed crash reporting! (And it's free for hobbyists!
That's it!
This time for real!