Flask Tour: быстрое начало работы

Python Flask

маршрутизация

У Flask есть только декоратор route() для привязки функции просмотра к URL-адресу.

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

Кроме того, мы также можем указать динамический URL.

Переменные можно добавить к URL-адресу, пометив часть URL-адреса как . Отмеченная часть передается функции в качестве ключевого аргумента.

# 比如获取一个用户详情的url

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

по умолчанию является строкой, если нам нужно указать тип параметра, мы можем передать:

<converter:variable_name>

Типы конвертеров в исполнении Flask:

转换器

Уникальный URL

Правила URL-адресов Flask основаны на модуле маршрутизации Werkzeug, который утверждает, что гарантирует элегантные и уникальные URL-адреса.

Взяв приведенный выше пример в качестве примера, если вы посетите

http://0.0.0.0:9999/user/2

Мы определяем маршрут «/user//». Если вы посещаете URL-адрес без / в конце, он будет перенаправлен на URL-адрес с косой чертой, что может сохранить уникальность URL-адреса и помочь поисковым системам избежать повторного индексирования одной и той же страницы. .

URL唯一性

Создать URL

Используйте url_for() для создания URL-адреса, который принимает имя функции в качестве первого параметра, а также принимает именованные параметры, соответствующие переменной части правила URL. Неизвестная переменная часть будет добавлена ​​в конец URL-адреса в качестве параметров запроса. .

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() 告诉 Flask 正在处理一个请求
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')

выход:

/user/2
/user/3?type=doctor

Почему бы не просто жесткий URL URL в шаблоне и создать его динамически, используя обратную функцию URL_FOR ()?

1. 反转通常比硬编码URL的描述性更好。
2. 未来更过URL, 你可以只在一个地方改变 URL,而不用到处替换。
3. URL创建会为你处理特殊字符的转义和Unicode数据,比较直观。
4. 生产的路径总是绝对路径,可以避免相对路径产生副作用。
5. 如果你的应用是放在URL根路径之外的地方(如在 /myapplication 中,不在 / 中), url_for() 会为你妥善处理。

Перейти и перенаправить

Прыжок (301) в основном используется для превращения старого веб-сайта в новый веб-сайт, прежде чем он будет заброшен, чтобы обеспечить доступ пользователя. Существует концепция, согласно которой на страницу всегда легко перейти.

Редирект (302) указывает на то, что страница переносится только временно, и часто использовать редирект не рекомендуется.

redirect(location) #默认是302
redirect(location, code=301) # 可以指定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')
    

HTTP-метод

Веб-приложения используют разные методы HTTP для обработки URL-адресов, и по умолчанию маршрут отвечает только на запросы GET. Различные методы HTTP можно обрабатывать с помощью параметра method декоратора route():

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

Краткое введение в общие методы HTTP и сценарии использования:

- GET: 获取资源, GET操作应该是幂等的

- HEAD: 想要获取信息, 但是只关心消息头, 应该可以把它当作GET来处理, 但是不返回内容。具有幂等性

- POST: 创建一个资源, 非幂等方法

- PUT: 完整的替换资源或者创建资源, 是幂等方法

- DELETE: 删除资源, 是幂等方法

- OPTIONS:获取资源所支持的所有HTTP方法

- PATCH: 局部更新, 非幂等方法

Идемпотентный метод HTTP — это метод HTTP, результат которого не меняется независимо от того, сколько раз он вызывается. Вызовете ли вы его один раз или вызовете сто раз, тысячу раз, результат один и тот же.

оКак понять идемпотентность RESTful.

В функции просмотра мы можем использовать следующие методы для определения метода HTTP-запроса.

from flask import Flask, request

request.method # 获取当前请求的方法

статические файлы

Для динамических веб-приложений также требуются статические файлы, обычно файлы CSS и JavaScript. В идеале ваш сервер уже настроен для обслуживания ваших статических файлов. Но во время разработки Flask также может хорошо справляться с этой задачей. Просто создайте папку с именем static рядом с вашим пакетом или модулем. Статические файлы находятся в /static приложения.

在实例化Flask时候如果你查看源码你会发现
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
    ):

static — каталог статических файлов по умолчанию.

# 这样是可以直接访问到静态文件的

http://0.0.0.0:9999/static/app.css

Мы не рекомендуем писать непосредственно в шаблоне мертвых внутри статического пути файла, вы должны использовать URL_FOL ()

url_for('static', filename='app.css')

Конечно, мы также можем настроить реальный каталог статических файлов.

app = Flask(__name__, static_folder='/tmp')

шаблон рендеринга

Flask по умолчанию предоставляет механизм шаблонов Jinja2.

Шаблоны можно визуализировать с помощью метода render_template(), все, что вам нужно сделать, это указать имя шаблона и переменные, которые необходимо передать в качестве параметров в шаблон. Вот простой пример рендеринга шаблона:

templates — это имя каталога по умолчанию для Flask.

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

Внутри шаблона вы можете получить доступ к объектам request, session и g таким же образом, как и функция get_flashed_messages() [g хранит глобальные переменные текущего запроса, и разные запросы будут иметь разные глобальные переменные].

Документация Jinja2Я не буду вдаваться в подробности здесь, я напишу отдельную статью позже, когда у меня будет время.Теперь, когда перед и зад разделены, шаблоны используются меньше.

Манипулировать объектом запроса

В Flask информация о запросе предоставляется запросом глобального объекта. Если вы знакомы с основами Python, вам может быть интересно: как он может быть потокобезопасным, если этот объект является глобальным?

from flask import request

Некоторые объекты во Flask являются глобальными, но не в обычном смысле. Эти объекты фактически являются прокси для локальных объектов в определенном контексте. Какой глоток! Но все же легко понять.

С того момента, как приложение Flask считывает конфигурацию и запускается, оно входит в контекст приложения, где мы можем получить доступ к файлам конфигурации, открыть файлы ресурсов и обратно построить URL-адреса с помощью правил маршрутизации. Когда запрос поступает и начинает обрабатываться, он входит в контекст запроса, в котором мы можем получить доступ к информации, содержащейся в запросе, такой как метод HTTP, поля формы и т. д.

Хоть request и глобальная переменная, это всего лишь прокси, если хотите покопаться в деталях, то можете прочитать эту статьюКонтекстный механизм Flask.

Понимание контекста запроса Flask.

- 通过使用 method 属性可以操作当前请求方法

- 通过使用 form 属性处理表单数据(在 POST 或者 PUT 请求 中传输的数据), 当 form 属性中不存在这个键时会发生什么?会引发一个 KeyError 。 
如果你不像捕捉一个标准错误一样捕捉 KeyError ,那么会显示一个 HTTP 400 Bad Request 错误页面。

- 要操作 URL (如 ?key=value )中提交的参数可以使用 args 属性:
searchword = request.args.get('key', '')

отклик

Возвращаемое значение функции представления преобразуется в объект ответа.

Например:

# 你会发现页面里面没有文字显示, 只有h3标签
@app.route('/')
def index():
    return '<h3></h3>'

Вы посмотрите на исходный код app.route() и найдете

# route装饰器只是一个语法糖, 实际执行的还是add_url_rule()
def decorator(f):
    endpoint = options.pop('endpoint', None)
    self.add_url_rule(rule, endpoint, f, **options)
    return f
return decorator

Логика трансформации следующая:

1. 如果返回的是一个合法的响应对象, 它会从视图直接返回
2. 如果返回的是一个字符串, 会用字符串数据和默认参数来创建以字符串为主体, 状态码为200,MIME类型的text.html的werkzeug.wrappers.Response响应对象。

3. 如果返回的是一个元组, 且元组的元素可以提供额外的信息, 这样的元组格式为(response, status, headers)。它们会覆盖默认的配置。

4. 如果上述条件都不对, Flask会假设返回值是一个合法的WSGI应用程序, 并通过Response.force_type(rv, request.environ)转化为请求对象

Пример:

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

Мы также можем отображать с помощью метода make_response.

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

Используя метод make_response, мы можем установить файлы cookie, заголовки и т. д.

Глядя на исходный код метода make_response, на самом деле очень легко понять.

def make_response(*args):
    # 根据args传参情况进行不同处理
    if not args:
        return current_app.response_class()
    if len(args) == 1:
        args = args[0]
    return current_app.make_response(args)
def make_response(self, rv):
    status = headers = None
    # unpack tuple returns
    # 传入的是元组
    if isinstance(rv, tuple):
        len_rv = len(rv)
        # 格式(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