Python Flask framework @ app. route usage tutorial, flask@app.route
In my previous article, I built a framework to simulate the behavior of the first example on the Flask Website "@ app. route.
If you miss the article "this is not magic", click here.
In this article, we plan to slightly tune the high difficulty and add variable parameters to our URLs. At the end of this article, we will support the behavior expected by the following code segments.
app = Flask(__name__) @app.route("/hello/<username>")def hello_user(username): return "Hello {}!".format(username)
In this way, the following path instance (path ):
/Hello/ains
The above path will be matched, and the output to us is
Hello ains!
Express our path in the form of regular expressions.
Now we will allow dynamic changes to our URLs, and we will no longer be able to directly compare the paths registered using "@ app. route ()" with the path instance.
What will we use to replace it? We need to use a regular expression, so that we can match the path as a pattern, instead of comparing it with a fixed string.
I am not going to discuss the details of Regular Expressions in this article, but if you need a review, you can click this website.
Then, our first step is to convert our path to the regular expression mode so that we can match the input path instance. We will also use this regular expression to extract the variables we are interested in.
What is the length of the regular expression that matches the path "/hello?
A simple regular expression such as "^/hello/(. +) $" is a good start. Let's take a look at how it works with the code:
import re route_regex = re.compile(r"^/hello/(.+)$")match = route_regex.match("/hello/ains") print match.groups()
The output is as follows:
('Ains ',)
Yes, but ideally we want to maintain the first group of links we have matched and identify "username" from the path "/hello ".
Naming capture group
Fortunately, regular expressions also support naming and capturing groups, allowing us to assign a name to a matching group, and we can retrieve it after reading our matching.
We can use the following symbol to give the first example to identify the capture group of "username.
/hello/(<?P<username>.+)"
Then we can use the groupdict () method for our regular expression to treat all capture groups as a dictionary, and the group names correspond to matching values.
The following code is provided:
route_regex = re.compile(r'^/hello/(?P<username>.+)$')match = route_regex.match("/hello/ains") print match.groupdict()
The following dictionary is output for us:
{'Username': 'ains '}
Now, with the regular expression format we need and how to use them to match the input URLs, the last thing left is to write a method, convert the declared paths to their equivalent regular expression patterns.
To do this, we will use another regular expression (then it will be all regular expressions). to convert the variables in our path to the regular expression mode, here we will convert "" into "(? P. + )".
It sounds too simple! We can implement it with only one new line of code.
def build_route_pattern(route): route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route) return re.compile("^{}$".format(route_regex)) print build_route_pattern('/hello/<username>')
Here we use a regular expression to represent all the modes that appear (a string contained in angle brackets), which is equivalent to its regular expression naming group.
The first parameter of re. sub is placed in parentheses to assign it to the first matching group. In our second parameter, we can use the content of the first matching group by writing 1 (2 will be the content of the second matching group, and so on .......)
Then, the input mode
/hello/<username>
The regular expression will be given:
^/hello/(?P<username>.+)$
Innovation
Let's take a look at the simple NotFlask class we wrote last time.
class NotFlask(): def __init__(self): self.routes = {} def route(self, route_str): def decorator(f): self.routes[route_str] = f return f return decorator def serve(self, path): view_function = self.routes.get(path) if view_function: return view_function() else: raise ValueError('Route "{}"" has not been registered'.format(path)) app = NotFlask() @app.route("/")def hello(): return "Hello World!"
Now we have a new improvement method to match the input path. We intend to remove the native dictionary we used in the previous implementation.
Let's start with the transformation of our function to facilitate the addition of paths, so that we can use (pattern, view_function) to replace the list to save our path in the dictionary.
This means that when a programmer uses @ app. route () describe a function. We will try to compile their path into a regular expression and store it as a decoration function in our new path list.
Let's look at the implementation code:
class NotFlask(): def __init__(self): self.routes = [] # Here's our build_route_pattern we made earlier @staticmethod def build_route_pattern(route): route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route) return re.compile("^{}$".format(route_regex)) def route(self, route_str): def decorator(f): # Instead of inserting into a dictionary, # We'll append the tuple to our route list route_pattern = self.build_route_pattern(route_str) self.routes.append((route_pattern, f)) return f return decorator
We also plan to need a get_route_match method, and give it a path instance. We will try and find a matched view_function, or return None if one cannot be found.
However, if a match is found, in addition to view_function, we also need to return something that contains the dictionary of the previously captured matching group, we need it to pass correct parameters for the view function.
Well, our get_route_match will be long like this:
def get_route_match(path): for route_pattern, view_function in self.routes: m = route_pattern.match(path) if m: return m.groupdict(), view_function return None
Now we are almost done. The last step is to find out the method for calling view_function and use the regular expression to match the correct parameters of the group dictionary.
Several Methods for calling a function
Let's review how different methods call a python function.
For example:
def hello_user(username): return "Hello {}!".format(username)
The simplest (perhaps as you are familiar with) method is to use regular parameters, where the order of parameters matches the order of the functions we define.
>>> hello_user("ains")Hello ains!
Another way to call a function is to use a keyword parameter. Keyword parameters can be specified in any order and are suitable for functions with many optional parameters.
>>> hello_user(username="ains")Hello ains!
In Python, the last method to call a function is to use the keyword parameter dictionary, which corresponds to the parameter name. We told Python to unpack a dictionary and use two asterisks "**" to treat it as a function's keyword parameter. The following code snippet is exactly the same as the code snippet above. Now we can use dictionary parameters to dynamically create it at runtime.
>>> kwargs = {"username": "ains"}>>> hello_user(**kwargs)Hello ains!
Okay, remember the groupdict () method above? Is that the guy who returns {"username": "ains"} after the regular expression matches? Now that we understand kwargs, we can easily pass dictionary matching to our view_function to complete NotFlask!
So let's put all these into our final class.
class NotFlask(): def __init__(self): self.routes = [] @staticmethod def build_route_pattern(route): route_regex = re.sub(r'(<w+>)', r'(?P1.+)', route) return re.compile("^{}$".format(route_regex)) def route(self, route_str): def decorator(f): route_pattern = self.build_route_pattern(route_str) self.routes.append((route_pattern, f)) return f return decorator def get_route_match(self, path): for route_pattern, view_function in self.routes: m = route_pattern.match(path) if m: return m.groupdict(), view_function return None def serve(self, path): route_match = self.get_route_match(path) if route_match: kwargs, view_function = route_match return view_function(**kwargs) else: raise ValueError('Route "{}"" has not been registered'.format(path))
Next, let's see the following code snippet to witness a miracle:
app = NotFlask() @app.route("/hello/")def hello_user(username):return "Hello {}!".format(username) print app.serve("/hello/ains")
We will get the output:
Hello ains!