Привет всем. В этой статье я расскажу о создании простых асинхронных проектов с помощью фреймворка Sanic.
Вступление
Sanic - это очень похожий на флягу веб-сервер Python с открытым исходным кодом и веб-фреймворк с более чем 10 тысячами звезд, который написан для быстрой работы. Он позволяет использовать синтаксис async/await
, добавленный в Python 3.5 (подробнее), что делает ваш код неблокирующим и быстрым.
У Sanic есть неплохая документация, и она поддерживается сообществом для сообщества.
Цель проекта - предоставить простой способ запустить и запустить высокопроизводительный HTTP-сервер, который легко построить, расширить и, в конечном итоге, масштабировать.
Требования
Прежде чем мы начнем, давайте установим несколько пакетов и убедимся, что у нас есть все готово для развития этого проекта.
Примечание: исходный код доступен в моем репозитории github.com. Для каждого шага есть соответствующая фиксация.
Предпосылки:
- Python3.6 +
- Pipenv (можно использовать любой другой установщик пакета)
- PostgreSQL (для базы данных также может быть MySQL или SQLite)
Пакеты:
- Secure - это легкий пакет, который добавляет дополнительные заголовки безопасности и атрибуты cookie для веб-фреймворков Python.
- Environs - это библиотека Python для анализа переменных среды. Это позволяет вам хранить конфигурацию отдельно от вашего кода в соответствии с методологией Двенадцатифакторного приложения.
- Sanic-envconfig - это расширение, которое помогает вам добавить переменные командной строки и среды в вашу конфигурацию Sanic.
- Базы данных - это пакет Python, который позволяет выполнять запросы с использованием мощного языка выражений SQLAlchemy Core и обеспечивает поддержку PostgreSQL, MySQL и SQLite.
Давайте создадим пустой каталог и инициализируем там пустой Pipfile
.
pipenv -- python python3.6
Установите все необходимые пакеты, используя приведенные ниже команды pipenv.
pipenv install sanic secure environs sanic-envconfig
Для базы данных:
pipenv install databases[postgresql]
Возможные варианты: postgresql, mysql, sqlite.
Состав
Теперь давайте создадим несколько файлов и папок, в которых мы будем писать наш код.
├── .env
├── Pipfile
├── Pipfile.lock
├── setup.py
└── project
├── __init__.py
├── __main__.py
├── main.py
├── middlewares.py
├── routes.py
├── settings.py
└── tables.py
Мы будем использовать файл setup.py
, чтобы сделать папку project
доступной как пакет в нашем коде.
from setuptools import setup
setup(
name='project',
)
Установка…
pipenv install -e .
В файле .env
мы сохраним некоторые глобальные переменные, например URL-адрес подключения к базе данных.
__main__.py
создан для того, чтобы сделать наш project
пакет исполняемым из командной строки.
pipenv run python -m project
Инициализация
Давайте сделаем наш первый вызов в файле __main__.py.
from project.main import init init()
Это начало нашего приложения. Теперь нам нужно создать функцию init
внутри файла main.py.
from sanic import Sanic
app = Sanic(__name__)
def init():
app.run(host='0.0.0.0', port=8000, debug=True)
Просто создав приложение из класса Sanic, мы можем запустить его, указав хост, порт и необязательный аргумент ключевого слова debug.
Выполняется…
pipenv run python -m project
Вот как должен выглядеть успешный результат в вашем приложении Sanic. Если вы откроете в браузере http://0.0.0.0:8000, вы увидите
Ошибка: запрошенный URL / не найден
Мы еще не создали никаких маршрутов, так что пока все в порядке. Ниже мы добавим несколько маршрутов.
Настройки
Теперь мы можем изменить среду и настройки. Нам нужно добавить некоторые переменные в файл .env, прочитать их и передать в конфигурацию приложения Sanic.
.env.
DEBUG=True HOST=0.0.0.0 POST=8000
Конфигурация…
from sanic import Sanic
from environs import Env from project.settings import Settingsapp = Sanic(__name__)
def init():
env = Env() env.read_env() app.config.from_object(Settings)app.run( host=app.config.HOST, port=app.config.PORT, debug=app.config.DEBUG,
auto_reload=app.config.DEBUG,)
settings.py.
from sanic_envconfig import EnvConfig class Settings(EnvConfig): DEBUG: bool = True HOST: str = '0.0.0.0' PORT: int = 8000
Обратите внимание, что я добавил необязательный аргумент auto_reload, который активирует или деактивирует автоматическую перезагрузку.
База данных
Пришло время создать базу данных.
Прежде чем мы продолжим, сделаем небольшое замечание о пакете баз данных:
Пакет баз данных использует asyncpg , который представляет собой библиотеку асинхронного интерфейса для PostgreSQL. Это довольно быстро. См. Результаты ниже.
Мы будем использовать два слушателя Sanic, где мы будем указывать операции подключения и отключения базы данных. Вот слушатели, которые мы собираемся использовать:
- after_server_start
- after_server_stop
main.py файл.
from sanic import Sanic
from databases import Database from environs import Env from project.settings import Settingsapp = Sanic(__name__)
def setup_database(): app.db = Database(app.config.DB_URL) @app.listener('after_server_start') async def connect_to_db(*args, **kwargs): await app.db.connect() @app.listener('after_server_stop') async def disconnect_from_db(*args, **kwargs): await app.db.disconnect()def init():
env = Env() env.read_env() app.config.from_object(Settings) setup_database()app.run( host=app.config.HOST, port=app.config.PORT, debug=app.config.DEBUG,
auto_reload=app.config.DEBUG,)
Еще раз. Нам нужно указать DB_URL в настройках проекта и окружении.
.env.
DEBUG=True HOST=0.0.0.0 POST=8000 DB_URL=postgresql://postgres:postgres@localhost/postgres
И файл settings.py.
from sanic_envconfig import EnvConfig class Settings(EnvConfig): DEBUG: bool = True HOST: str = '0.0.0.0' PORT: int = 8000 DB_URL: str = ''
Убедитесь, что DB_URL правильный и ваша база данных работает. Теперь вы можете получить доступ к базе данных с помощью app.db. См. более подробную информацию в следующем разделе.
Таблицы
Теперь у нас есть соединение с нашей базой данных, и мы можем попытаться выполнить несколько SQL-запросов.
Давайте объявим таблицу в файле tables.py с помощью SQLAlchemy.
import sqlalchemy metadata = sqlalchemy.MetaData() books = sqlalchemy.Table( 'books', metadata, sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True), sqlalchemy.Column('title', sqlalchemy.String(length=100)), sqlalchemy.Column('author', sqlalchemy.String(length=60)), )
Здесь я предполагаю, что у вас уже есть перенесенная база данных с таблицей книги в ней. Для создания миграции базы данных я рекомендую вам использовать Alembic, легкий и простой в использовании инструмент, который вы можете использовать с SQLAlchemy Database Toolkit для Python.
Теперь мы можем использовать любые запросы ядра SQLAlchemy. Ознакомьтесь с некоторыми примерами ниже.
# Executing many query = books.insert() values = [ {"title": "No Highway", "author": "Nevil Shute"}, {"title": "The Daffodil", "author": "SkyH. E. Bates"}, ] await app.db.execute_many(query, values) # Fetching multiple rows query = books.select() rows = await app.db.fetch_all(query) # Fetch single row query = books.select() row = await app.db.fetch_one(query)
Маршруты
Теперь нам нужно настроить маршруты. Перейдем к routes.py и добавим новый маршрут для книг.
from sanic.response import json from project.tables import books def setup_routes(app): @app.route("/books") async def book_list(request): query = books.select() rows = await request.app.db.fetch_all(query) return json({ 'books': [{row['title']: row['author']} for row in rows] })
Конечно, нам нужно вызвать setup_routes в init, чтобы он заработал.
from project.routes import setup_routesapp = Sanic(__name__)
def init(): ...
app.config.from_object(Settings) setup_database() setup_routes(app) ...
Запрос…
$ curl localhost:8000/books
{"books":[{"No Highway":"Nevil Shute"},{"The Daffodil":"SkyH. E. Bates"}]}
Промежуточное ПО
Как насчет проверки заголовков ответа и посмотреть, что мы можем там добавить или исправить?
$ curl -I localhost:8000
Connection: keep-alive
Keep-Alive: 5
Content-Length: 32
Content-Type: text/plain; charset=utf-8
Как видите, нам нужны улучшения безопасности. Отсутствуют некоторые заголовки, такие как X-XSS-Protection, Strict-Transport-Security… , поэтому давайте позаботимся о них, используя комбинацию промежуточного программного обеспечения и безопасные пакеты.
middlewares.py.
from secure import SecureHeaders secure_headers = SecureHeaders() def setup_middlewares(app): @app.middleware('response') async def set_secure_headers(request, response): secure_headers.sanic(response)
Настройка промежуточного программного обеспечения в файле main.py.
from project.middlewares import setup_middlewaresapp = Sanic(__name__)
def init(): ...
app.config.from_object(Settings) setup_database() setup_routes(app) setup_middlewares(app) ...
Результат:
$ curl -I localhost:8000/books
Connection: keep-alive
Keep-Alive: 5
Strict-Transport-Security: max-age=63072000; includeSubdomains
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer, strict-origin-when-cross-origin
Pragma: no-cache
Expires: 0
Cache-control: no-cache, no-store, must-revalidate, max-age=0
Content-Length: 32
Content-Type: text/plain; charset=utf-8
Как я и обещал в начале, для каждого раздела в этой статье есть репозиторий github. Надеюсь, это небольшое руководство помогло вам начать работу с Sanic. В фреймворке Sanic есть еще много неисследованных функций, которые вы можете найти и проверить в документации.
Если у вас есть мысли по этому поводу, обязательно оставьте комментарий.
Если вы нашли эту статью полезной, дайте мне несколько аплодисментов 👏
Спасибо за прочтение. Быстро с Саником и удачи !!!