routing

Flask only has the route() decorator to bind view functions to urls.

@app.route('/user')
def hello_user():
    return 'Hello, user! '
Copy the code

Alternatively, we can specify dynamic urls.

Variables can be added to the URL by marking part of the URL as <variable_name>. The part of the tag is passed to the function as a keyword argument.

Get a url for user details

@app.route('/user/<id>/')
def hello_user(id):
    return 'Hello, userid: {0}! '.format(id)
Copy the code

<variable_name> is a string by default, and if we need to specify the parameter type we can do it by:

<converter:variable_name>
Copy the code

Flask performs the following types of converters:

The only Url

Flask’s URL rules are based on Werkzeug’s routing module, and insist on elegant and unique urls.

Take the example above if you visit

http://0.0.0.0:9999/user/2
Copy the code

The route we define is ‘/user//’, and visits to a URL that does not end with a slash will be redirected to a URL with a slash, which keeps the URL unique and helps search engines avoid indexing the same page twice.

To construct the URL

The URL is built using url_for(), which takes the function name as the first parameter and also the named parameter of the variable part of the corresponding URL rule. The unknown variable part is appended to the end of the URL as the query parameter.

from flask import Flask, url_for
app = Flask(__name__)

app.config['DEBUG'] = True

@app.route('/user/<int:id>')
def user(id):
    pass

# test_request_context() tells Flask that a request is being processed
with app.test_request_context():
    print(url_for('user', id=2))
    print(url_for('user', id=3, type='doctor'))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port='9999')
Copy the code

Output:

/user/2
/user/3?type=doctor
Copy the code

Why not write the URL to a template instead of building it dynamically using the inversion function url_for()?

1. Inversions are usually better descriptive than hard-coded urls. 2. Moving beyond urls, you can change urls in one place instead of replacing them everywhere. 3. URL creation handles special character escaping and Unicode data for you, which is intuitive. 4. The production path is always absolute to avoid side effects caused by relative paths. 5. If your application is located outside the URL root path (e.g. / myApplication, not /), url_for() will take care of it for you.Copy the code

Jump and redirect

Jump (301) is mostly used for the old site before the abandonment of the new site to ensure the access of users, there is a permanent page is easy to go concept.

Redirects (302) indicate that the page is a temporary diversion and are not recommended for frequent use.

redirect(location) The default value is 302
redirect(location, code=301) # you can specify code
Copy the code
from flask import Flask, url_for, render_template, redirect
app = Flask(__name__)

app.config['DEBUG'] = True

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    return render_template('login.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port='9999')
    
Copy the code

HTTP method

Web applications use different HTTP methods to handle urls, and by default, a route responds only to GET requests. You can use the methods parameter of the route() decorator to handle different HTTP methods:

@app.route('/login', methods=['GET'.'POST'])
Copy the code

The following describes common HTTP methods and application scenarios:

-head: Wants to GET information, but only cares about the header. It should be treated as a GET, but does not return the content. Idempotent -post: create a resource, non-idempotent -put: replace a resource completely or create a resource, idempotent -delete: DELETE a resource, idempotent -options: get all HTTP methods supported by the resource. -patch: Local update, non-idempotent methodCopy the code

HTTP idempotent methods are HTTP methods that have no different results no matter how many times they are called. Whether you call it once, a hundred times, a thousand times, it’s the same result.

On how to understand the idempotency of RESTful.

In view functions we can use the following method to determine HTTP request methods

from flask import Flask, request

request.method Get the current request method
Copy the code

Static files

Dynamic Web applications also require static files, typically CSS and JavaScript files. Ideally your server is already configured to serve your static files. But Flask does the job well during development. Just create a folder called static next to your package or module. Static files are in the application’s /static.

App = Flask(__name__) def __init__(self, import_name, static_url_path=None, static_folder='static',
        static_host=None,
        host_matching=False,
        subdomain_matching=False,
        template_folder='templates',
        instance_path=None,
        instance_relative_config=False,
        root_path=None
    ):
Copy the code

Static is the default directory for static files.

This allows direct access to static files

http://0.0.0.0:9999/static/app.css
Copy the code

We do not recommend killing static file paths in templates, instead use url_for() to generate paths

url_for('static', filename='app.css')
Copy the code

Of course we can also customize the real directory of static files

app = Flask(__name__, static_folder='/tmp')
Copy the code

Apply colours to a drawing template

Flask provides the Jinja2 template engine by default.

Render_template () is used to render the template, providing the template name and the variables you need to pass to the template as parameters. Here is a simple template rendering example:

Templates is the default directory name for Flask.

@app.route('/')
def index():
    return render_template('index.html', title='home')
Copy the code
<! DOCTYPE html> <html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
</head>
<body>
</body>
</html>
Copy the code

Request, session, and G objects can be accessed inside the template in the same way as the get_flashed_messages() function [G holds global variables for the current request, different requests have different global variables].

The Jinja2 document will not be detailed here, and I will write a separate article later when I am free. Now, it is separated from the front and the back, and templates are less used.

Manipulating the Request object

In Flask, request information is supplied by the global object Request. If you have some Python background, you might wonder: how can this object remain thread-safe if it is global?

from flask import request
Copy the code

Some objects are global in Flask, but not in the usual sense. These objects are actually proxies for local objects in a particular environment. True man! But it’s easy to understand.

From the start of a Flask App reading in the configuration and launching it, we enter the App Context, where we can access the configuration file, open the resource file, and reverse construct the URL through routing rules. When a Request comes in to be processed, it enters the Request Context, where we can access the information carried by the Request, such as HTTP Method, form fields, and so on.

Request is a global variable but only a proxy. For more details, check out Flask’s Context mechanism in this article.

Understand Flask’s request context.

- Use the method attribute to manipulate the current request method - use the form attribute to process form data (data transferred in POST or PUT requests). What happens when the key does not exist in the form attribute? Raises a KeyError. If you do not catch KeyError as if it were a standard error, an HTTP 400 Bad Request error page is displayed. - To manipulate urls (such as? Key =value); searchWord = request.args. Get (searchWord = request.args.'key'.' ')
Copy the code

The response

The return value of the view function is converted to a response object.

Such as:

# You will notice that there is no text display inside the page, only the H3 tag
@app.route('/')
def index():
    return '<h3></h3>'

Copy the code

If you look at the source code of app.route(), you will find it

The # route decorator is just a syntactic sugar, and it actually executes add_url_rule().
def decorator(f):
    endpoint = options.pop('endpoint', None)
    self.add_url_rule(rule, endpoint, f, **options)
    return f
return decorator
Copy the code

The conversion logic is as follows:

1. If a valid response object is returned, it will return 2 directly from the view. If the return is a string, use the string data and default parameters to create a string as the main body, a status code of 200, the MIME type of text, HTML werkzeug. The wrappers. The Response Response object. 3. If a tuple is returned and the element of the tuple can provide additional information, the format of the tuple is (Response, Status, headers). They override the default configuration. Flask assumes that the returned value is a valid WSGI application and transforms it into a request object via Response.force_type(RV, Request.environ)Copy the code

Example:

@app.route('/')
def index():
    return render_template('index.html'), 400
Copy the code

We can also display using the make_response method

from flask import Flask, url_for, render_template, redirect, request, make_response
app = Flask(__name__)

app.config['DEBUG'] = True

@app.route('/')
def index():
    response = make_response(render_template('index.html'), 400)
    return response

Copy the code

With the make_response method we can set cookies, headers, etc.

Looking at the source of the make_response method is actually pretty straightforward.

def make_response(*args):
    # Different processing according to arGS parameter transfer
    if not args:
        return current_app.response_class()
    if len(args) == 1:
        args = args[0]
    return current_app.make_response(args)
Copy the code
def make_response(self, rv):
    status = headers = None
    # unpack tuple returns
    # the tuple is passed in
    if isinstance(rv, tuple):
        len_rv = len(rv)
        Format: response, status, headers
        if len_rv == 3:
            rv, status, headers = rv
        # decide if a 2-tuple has status or headers
        elif len_rv == 2:
            if isinstance(rv[1], (Headers, dict, tuple, list)):
                rv, headers = rv
            else:
                rv, status = rv
        # other sized tuples are not allowed
        else:
            raise TypeError(
                'The view function did not return a valid response tuple.'
                ' The tuple must have the form (body, status, headers),'
                ' (body, status), or (body, headers).'
            )

    # the body must not be None
    if rv is None:
        raise TypeError(
            'The view function did not return a valid response. The'
            ' function either returned None or ended without a return'
            ' statement.'
        )

    # make sure the body is an instance of the response class
    if not isinstance(rv, self.response_class):
        if isinstance(rv, (text_type, bytes, bytearray)):
            # let the response class set the status and headers instead of
            # waiting to do it manually, so that the class can handle any
            # special logic
            rv = self.response_class(rv, status=status, headers=headers)
            status = headers = None
        else:
            # evaluate a WSGI callable, or coerce a different response
            # class to the correct type
            try:
                rv = self.response_class.force_type(rv, request.environ)
            except TypeError as e:
                new_error = TypeError(
                    '{e}\nThe view function did not return a valid'
                    ' response. The return type must be a string, tuple,'
                    ' Response instance, or WSGI callable, but it was a'
                    ' {rv.__class__.__name__}.'.format(e=e, rv=rv)
                )
                reraise(TypeError, new_error, sys.exc_info()[2])

    # prefer the status if it was provided
    if status is not None:
        if isinstance(status, (text_type, bytes, bytearray)):
            rv.status = status
        else:
            rv.status_code = status

    # extend existing headers with provided headers
    if headers:
        rv.headers.extend(headers)

    return rv
Copy the code