Flask routes - URL mapping - views
Flask allows you to have a very flexible mapping of URL pathes to function calls. Let's see what can you do.
A few notes for placeholders:
- Simple <varname> captures anything except slashes.
- <string:varname> is the default prefix so we don't really need to include it. It captures everything except a slash /
- <int:varname> accepts various unicode digits as well
- <float:varname> accpets a floating point numnber like 123.4, but it does not accept 1. or .1 or a simple integer like 42 without any dot.
- <path:varname> accepts any character, including slashes /.
This a sample Flask file with several possible path mappings.
Including one where we created our own rule to match routes that contain a valid IPv4 address.
examples/flask/routes/app.py
from flask import Flask demoapp = Flask(__name__) @demoapp.route("/") def main(): return "Main page" @demoapp.route("/some/path") def some_path(): return "A fixed path" @demoapp.route("/user/<name>") def user_name(name): return "The name is {}".format(name) @demoapp.route("/title/<string:name>") def title(name): return "The title is {}".format(name) @demoapp.route("/id/<int:uid>") def user_id(uid): return "The uid is {}".format(uid) @demoapp.route("/coord-x/<float:x>") def coord_x(x): return "The x coordinate is {}".format(x) @demoapp.route("/place/<path:location>") def place(location): return "The location is {}".format(location) @demoapp.route("/coord/<float:x>/<float:y>") def coord(x, y): return "The coordinate is ({}, {})".format(x, y) @demoapp.route("/street/<name>/zip/<code>") def machine(name, code): return "The input is {} and {}".format(name, code) import converters demoapp.url_map.converters['ipv4'] = converters.IPv4Converter @demoapp.route('/ip/<ipv4:address>') def ip_address(address): return "The IP is {}".format(address)
examples/flask/routes/converters.py
from werkzeug.routing import BaseConverter, ValidationError import re class IPv4Converter(BaseConverter): def to_python(self, value): match = re.search(r'(\d+)\.(\d+)\.(\d+)\.(\d+)$', value) if not match: raise ValidationError() for i in [1, 2, 3, 4]: if not 0 <= int(match.group(i)) <= 255: raise ValidationError() return value
You can start the application by running
FLASK_APP=app FLASK_DEBUG=1 flask run
And then you can access it using some of the following URLs:
http://localhost:5000/ http://localhost:5000/some/path http://localhost:5000/user/foobar http://localhost:5000/user/ Not Found!
Feel free to try anything you like. Below you'll find the test cases that will help you see which route matches which URLs.
You can use more than one placeholders in a route definition and you can even have fixed elements between the placeholders, though I am not sure when would you want to have that.
Testing the routes
I've also included sample code that will test each one of the given routes.
examples/flask/routes/test_app.py
import app def test_static_routes(): myapp = app.demoapp.test_client() rv = myapp.get('/') assert rv.status == '200 OK' assert b'Main page' == rv.data rv = myapp.get('/some/path') assert rv.status == '200 OK' assert b'A fixed path' == rv.data def test_one_name(): myapp = app.demoapp.test_client() rv = myapp.get('/user/foobar') assert rv.status == '200 OK' assert b'The name is foobar' == rv.data rv = myapp.get('/user/foo-bar .$') # accepts any character assert rv.status == '200 OK' assert b'The name is foo-bar .$' == rv.data rv = myapp.get('/user/foo/bar') # except a slash assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data print(rv.data) rv = myapp.get('/user/') # and there must be *something* assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data print(rv.data) def test_one_string(): myapp = app.demoapp.test_client() rv = myapp.get('/title/Hello World!') assert rv.status == '200 OK' assert b'The title is Hello World!' == rv.data def test_one_int(): myapp = app.demoapp.test_client() rv = myapp.get('/id/42') assert rv.status == '200 OK' assert b'The uid is 42' == rv.data rv = myapp.get('/id/0') assert rv.status == '200 OK' assert b'The uid is 0' == rv.data rv = myapp.get('/id/x42') # only accepts digits assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data print(rv.data) rv = myapp.get('/id/-1') # not even something that looks a negative int assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data rv = myapp.get('/id/1.2') # or a dot assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data rv = myapp.get('/id/%D9%A4') # ٤ ARABIC-INDIC DIGIT FOUR assert rv.status == '200 OK' assert b'The uid is 4' == rv.data rv = myapp.get('/id/٤') # ٤ ARABIC-INDIC DIGIT FOUR assert rv.status == '200 OK' assert b'The uid is 4' == rv.data rv = myapp.get('/id/߅') # NKO DIGIT FIVE assert rv.status == '200 OK' assert b'The uid is 5' == rv.data def test_float(): myapp = app.demoapp.test_client() rv = myapp.get('/coord-x/4.2') assert rv.status == '200 OK' assert b'The x coordinate is 4.2' == rv.data rv = myapp.get('/coord-x/42') # does not accept simple digits assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data rv = myapp.get('/coord-x/1.2.3') # nor more than one dot assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data rv = myapp.get('/coord-x/.2') assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data rv = myapp.get('/coord-x/2.') assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data def test_path(): myapp = app.demoapp.test_client() rv = myapp.get('/place/a/b/c') assert rv.status == '200 OK' assert b'The location is a/b/c' == rv.data rv = myapp.get('/place/a') assert rv.status == '200 OK' assert b'The location is a' == rv.data rv = myapp.get('/place/foo/bar/') assert rv.status == '200 OK' assert b'The location is foo/bar/' == rv.data def test_multiple(): myapp = app.demoapp.test_client() rv = myapp.get('/coord/4.2/1.3') assert rv.status == '200 OK' assert b'The coordinate is (4.2, 1.3)' == rv.data def test_strange_multiple(): myapp = app.demoapp.test_client() rv = myapp.get('/street/foo/zip/42') assert rv.status == '200 OK' assert b'The input is foo and 42' == rv.data def test_ip_address(): myapp = app.demoapp.test_client() rv = myapp.get('/ip/1.2.3.4') assert rv.status == '200 OK' assert b'The IP is 1.2.3.4' == rv.data rv = myapp.get('/ip/1.2.3') assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data rv = myapp.get('/ip/1.2.0.256') assert rv.status == '404 NOT FOUND' assert b'404 Not Found' in rv.data
As they are writtent these two files need to be in the same directory. You can cd in that directory on your terminal and run:
pytest
If you run it with the -s flag then you'll also see the output from the print statements in the test file.
pytest -s
Comments
can I write on the one of html page which is in templates folder from any function in which is written in main flask file?
Published on 2019-02-09