Notes on Routing¶
Target URL Defined by Decorator¶
Routing in Flask is done by adding a decorator to a handler function. The decorator follows a URL target syntax. For example:
#
# Recipe display page
#
@views.route('/display/<int:rid>')
def display(rid):
# function code continues...
This prototype means that any requests coming in using the target /display/X where X is an integer
(e.g. https://recipes.pdxitservices.com/display/12 will be passed to the display(rid) function as
display(12) )
“views” in this case is just the name of the blueprint that is registered to handle this route.
See the section on blueprints for more information.
Decorators in Python @make_it_fancy¶
Decorators provide a means to add more functionality to an established function without altering it.
This is done by wrapping the function inside another one. Additional code can then be placed in the wrapper
along with the original function. If you want to decorate a window in your house, you can place
curtains or blinds, add window stickys, etc. without altering the window. This is one way to
view how decorators work.
Decorators are implemented by placing the decorator name (begins with @ symbol) just above the function
definition. Every time the original function is called, Python will automatically pass the original function
to the decorator function.
For a minimal example, let’s say we have a function add_two(x,y) that adds two numbers and prints the result.
We will decorate it by having it print “Hello!” before the result and “Goodbye!” after.
Passing functions to other functions? Yes. Functions in Python are first-class citizens meaning
we can handle them just like any regular variable (string, int, list, dict, etc.). We can define foo = func,
after which we can call func() by using the syntax foo(). This includes passing a function to another one,
which is what we will do for creating a decorator. The basic pattern is shown in the code block below:
# definition of the decorator
def hello_then_goodbye(func):
def wrapper(*args, **kwargs):
print("Hello!")
func(*args, **kwargs)
print("Goodbye!")
return wrapper
# Now implement the decorated function
@hello_then_goodbye
def add_two(x, y):
print("Your result is: " + str(x+y))
# Output:
# >>> add_two(1,2)
# Hello!
# Your result is: 3
# Goodbye!
We called add_two(1,2) but what we get back is the result of wrapper().
What are these *args and **kwargs?¶
The code above makes use of *args and **kwargs which simply means a variable number of arguments.
These appear all the time in Python code so it’s useful to understand their meaning.
In Python we can use positional arguments where
you simply pass the variables in the expected order e.g. c = create_fruit(‘banana’, ‘yellow’),
or we can use keyword arguments to pass them in any order
e.g. c = create_fruit(color=’yellow’, name=’banana’). The keywords (color, name in this case)
must match the argument names used in the function signature.
The single asterisk * is used for variable number of positional arguments, and the double
asterisk ** is used for a variable number of keyword arguments. By convention these are
named *args (arguments) and **kwargs (keyword arguments) but you could just as easily call
them *vars and **vars. You should stick to naming them *args and **kwargs since that’s
the convention and other programmers will know what you’re doing. Don’t confuse this syntax with
C/C++ pointers. Python doesn’t have pointers. (Not to get off-trail too much here but Python
passes arguments by object reference, so while you are passing a reference to an existing object,
the reference is passed by value, and the behavior is similar to passing by value in C/C++)
Unless we write a custom wrapper argument list for every function we want to decorate,
the wrapper in our decorator function won’t know ahead of time how many arguments there will be.
We use *args and **kwargs to allow for a variable number of arguments,
and they can be positional or named. We could just use *args in the argument list but then it
would not accept keyword arguments, similarly if we just used **kwargs it would require keyword
named arguments. By providing both, we cover any case. Whatever gets passed to our function will be passed
to the wrapper. Then the wrapper passes them to the interior (original) function. This lets us
re-use our decorator wherever we want without changing the argument list in our decorator
function.
Why does Flask use these decorators?¶
That aside on decorators was just a mini-tutorial to explain a concept often encountered in
Python code, so it’s useful to know. For our specific use here (Flask), it uses the decorator
syntax in order to register the function for a given URL target.
This creates lookup table of URL’s to match with the function
that will handle the HTTP request. If you want the nitty gritty details of exactly what’s
going on, you’ll have to look inside the Flask source code, but it’s beyond the scope here.
We don’t have to understand the implementation in order to use the Flask API.