Channels django: Django Channels — Channels 3.0.2 documentation
Django Channels – ответ современному вебу
В мире Django набирает популярность дополнение Django Channels. Эта библиотека должна принести в Django асинхронное сетевое программирование, которое мы так долго ждали. Артём Малышев на Moscow Python Conf 2017 объяснил, как это делает первая версия библиотеки (сейчас автор уже запилил channels2), зачем она это делает и делает ли вообще.
Прежде всего, дзен Python говорит, что любое решение должно быть единственное. Поэтому в Python всего минимум по три. Сетевых асинхронных фреймворков уже существует большое множество:
- Twisted;
- Eventlet;
- Gevent;
- Tornado;
- Asyncio.
Казалось бы, зачем писать еще одну библиотеку и надо ли вообще.
О спикере: Артём Малышев независимый Python разработчик. Занимается разработкой распределённых систем, выступает на конференциях по Python. Артёма можно найти по никнейму PROOFIT404 на Github и в социальных сетях.
Django синхронный по определению. Если мы говорим об ORM, то синхронно обратиться к базе во время attribute access, когда мы пишем, например, post.author.username, ничего не стоит.
К тому же Django — это WSGI фреймворк
WSGI
WSGI — это синхронный интерфейс для работы с веб-серверами.
def app (environ, callback) :
status, headers = '200 OK', []
callback (status, headers)
return ['Hello world!\n']
Его основной особенностью является то, что у нас есть функция, которая принимает аргумент и сразу же возвращает значение. Это все, что может ждать от нас веб-сервер. Никакой асинхронщинной и не пахнет.
Сделано это было давным-давно, в далеком 2003 году, когда web был простой, пользователи читали в интернете всякие новости, заходили в гостевые книги. Достаточно было просто принять запрос и обработать его. Дать ответ и забыть о том, что этот пользователь вообще был.
Но, на секундочку, сейчас не 2003 год, поэтому пользователи хотят от нас намного большего.
Они хотят Rich web application, живой контент, хотят, чтобы приложение работало замечательно на десктопе, на лэптопе, на других топах, на часах. Самое главное, пользователи не хотят нажимать F5, потому что, например, на планшетах нет такой кнопки.
Веб-браузеры, естественно, идут нам на встречу — они добавляют новые протоколы и новые возможности. Если бы мы с вами разрабатывали только фронтенд, то мы просто брали бы браузер как платформу и использовали бы его core фичи, поскольку он готов их нам предоставить.
Но, для программистов бэкенда все очень сильно поменялось. Веб-сокеты, HTTP2 и тому подобное — это огромная боль с точки зрения архитектуры, потому что это долгоживущие коннекты со своими состояниями, которые нужно как-то обрабатывать.
Именно эту проблему и пытается решить Django Channels для Django. Эта библиотека призвана дать вам возможность обрабатывать коннекты, оставив Django Core, к которому мы привыкли, абсолютно неизменным.
Сделал это замечательный человек Andrew Godwin, обладатель ужасного английского акцента, который говорит очень быстро. Он должен быть вам известен по таким штукам, как давно забытый Django South и Django Migrations, которые пришли к нам с версии 1.7. С тех пор, как он починил миграции для Django, он занялся тем, что начал чинить веб-сокеты и HTTP2.
Каким образом он это сделал? Давным-давно по интернету ходила такая картинка: пустые квадратики, стрелочки, надпись «Хорошая архитектура» — вписываете в эти квадратики свои любимые технологии, получаете сайт, который хорошо масштабируется.
Andrew Godwin вписал в эти квадратики сервер, который стоит фронтом и принимает любые запросы, будь они асинхронные, синхронные, e-mail, что угодно. Между ними стоит так называемый Channel Layer, который принятые сообщения хранит в формате, доступном для пула синхронных воркеров. Как только ассинхронный коннект что-то нам прислал, мы записываем это в Channel Layer, а далее синхронный воркер может его забирать оттуда и обрабатывать точно так же, как это делает любая Django View или что угодно другое, синхронно. Как только синхронный код отправил обратно в Channel Layer ответ, асинхронный сервер будет его отдавать, стримить, делать все, что ему нужно. Тем самым производится абстракция.
Это подразумевает несколько реализаций и в продакшене предлагается использовать Twisted, как асинхронный сервер, который реализует фронтенд для Django, и Redis, который будет тем самым каналом общения между синхронным Django и асинхронным Twisted.
Хорошая новость: для того, чтобы использовать Django Channels, вам вообще не нужно знать ни Twisted, ни Redis — это все детали реализации. Это будут знать ваш DevOps, или вы познакомитесь, когда будете чинить в три часа ночи упавший продакшен.
ASGI
Абстракция — это протокол под названием ASGI. Это стандартный интерфейс, который лежит между любым сетевым интерфейсом, сервером, будь то синхронный или асинхронный протокол и вашим приложением. Основным его понятием является канал.
Channel
Канал — это, упорядоченная по принципу first-in-first-out, очередь сообщений, которые обладают временем жизни. Эти сообщения могут быть доставлены ноль или один раз, и могут быть получены только одним Consumer’ом.
Consumers
В Consumer вы как раз пишете ваш код.
def ws_message (message) :
message.reply_channel.send ( {
'text': message.content ['text'],
} )
Функция, которая принимает message, может отослать несколько ответов, а может не отсылать ответ вообще. Очень похоже на view, единственная разница в том, что здесь нет функции return, тем самым мы можем говорить о том, сколько ответов мы возвращаем из функции.
Добавляем эту функцию в routing, например, вешаем ее на получение сообщения по веб-сокету.
from channels. routing import route
from myapp.consumers import ws_message
channel_routing = [
route ('websocket.receive' ws_message),
}
Прописываем это в Django settings, также, как прописывали бы база данных.
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'asgiref.inmemory',
'ROUTING': 'myproject.routing',
},
}
В проекте может быть несколько Channel Layers, точно также как может быть несколько баз данных. Эта штука очень похожа на db router, если кто-то этим пользовался.
Далее мы определяем наше ASGI приложение. В нем синхронизируется то, как запускается Twisted и то, как запускаются синхронные воркеры — им всем нужно это приложение.
import os
from channels.asgi import get_channel_layer
os.environ.setdefault(
'DJANGO_SETTINGS_MODULE',
'myproject.settings',
)
channel_layer = get_channel_layer()
После этого, деплоим код: запускаем gunicorn, стандартно отправляем HTTP-запрос, синхронно, с view, как привыкли. Запускаем асинхронный сервер, который будет стоять фронтом перед нашей синхронной Django, и воркеры, которые будут обрабатывать сообщения.
$ gunicorn myproject.wsgi
$ daphne myproject.asgi:channel_layer
$ django-admin runworker
Reply channel
Как мы видели, у message есть такое понятие как Reply channel. Зачем это нужно?
Сhannel однонаправленный, соответственно WebSocket receive, WebSocket connect, WebSocket disconnect — это общий channel на систему для входных сообщений. А Reply channel — это channel, который строго привязан к коннекту пользователя. Соответственно, message имеет входной и выходной канал. Эта пара позволяет вам идентифицировать от кого вам пришло это сообщение.
Groups
Группа — это набор каналов. Если мы посылаем сообщение в группу, то оно автоматически рассылается всем каналам этой группы. Это удобно, потому что никто не любит писать циклы for. Плюс реализация групп обычно сделана с помощью нативных функций Channel layer, поэтому работает быстрее, чем просто рассылка сообщений по одному.
from channels import Group
def ws_connect (message):
Group ('chat').add (message.reply_channel)
def ws_disconnect (message):
Group ('chat').discard(message.reply_channel)
def ws_message (message):
Group ('chat'). Send ({
'text': message.content ['text'],
})
Группы точно также добавляются в routing.
from channels.routing import route
from myapp.consumers import *
channel_routing = [
route ('websocket.connect' , ws_connect),
route ('websocket.disconnect' , ws_disconnect),
route ('websocket.receive' , ws_message),
]
И как только канал добавляется в группу, reply будет уходить всем пользователям, которые подключились к нашему сайту, а не только echo-ответ на нас самих.
Generic consumers
За что я люблю Django — это за декларативность. Точно также есть декларативные Consumers.
Base Consumer — базовый, умеет только маппить channel, который вы определили на какой-то свой метод и вызывать его.
from channels.generic import BaseConsumer
class MyComsumer (BaseConsumer) :
method_mapping = {
'channel.name.here': 'method_name',
}
def method_name (self, message, **kwargs) :
pass
Есть большое количество предопределенных consumers с заведомо дополненным поведением, таких как WebSocket Consumer, который заранее определяет, что он будет обрабатывать WebSocket connect, WebSocket receive, WebSocket disconnect. Можно сразу указать, в какие группы добавлять reply channel, и как только вы будете использовать self.send он будет понимать, послать это в группу или одному пользователю.
from channels.generic import WebsocketConsumer
class MyConsumer (WebsocketConsumer) :
def connection_groups (self) :
return ['chat']
def connect (self, message) :
pass
def receive (self, text=None, bytes=None) :
self.send (text=text, bytes=bytes)
Также есть вариант WebSocket consumer с JSON, то есть в receive будет приходить не текст, не байты, а уже распаршенный JSON — это удобно. /blog’ ),
]
Multiplexing
Если мы открываем несколько веб-сокетов, у каждого разное URI, и мы можем повесить на них несколько handler’ов. Но скажем честно, открывать несколько коннектов только для того, чтобы на бэкенде сделать что-то красивое, не похоже на инженерный подход.
Поэтому есть возможность по одному веб-сокету вызывать несколько handler’ов. Мы определяем такую WebsocketDemultiplexer, который оперирует понятием stream в рамках одного веб-сокета. Через этот stream он будет перенаправлять ваше сообщение в другой канал.
from channels import WebsocketDemultiplexer
class Demultiplexer (WebsocketDemultiplexer) :
mapping = {
'intval': 'binding.intval',
}
В routing мультиплексер добавляется точно также, как и в любой другой декларативный consumer route_class.
from channels import route_class, route
from .consumers import Demultiplexer, ws_message
channel_routing = [
route_class (Demultiplexer, path=’^/binding/’) ,
route ('binding. intval', ws_message ) ,
]
В message добавляется аргумент stream, чтобы мультиплексер мог понять, куда ему положить данный message. В аргументе payload присутствует все то, что уйдет в channel после того, как его обработает мультиплексер.
Очень важно отметить, что в Channel Layer, message попадет два раза: до мультиплексера и после мультиплексера. Таким образом, как только вы начинаете использовать мультиплексер, вы автоматически добавляете latency в свои запросы.
{
"stream" : "intval",
"payload" : {
…
}
}
Sessions
У каждого channel есть свои сессии. Это очень удобная штука, чтобы, например, хранить state между вызовами handler’ов. Сгруппировать их можно по reply channel, поскольку это идентификатор, который принадлежит пользователю. Сессия хранится в том же самом движке, в котором хранится обычная http сессия. По понятным причинам, signed cookie не поддерживается, их просто нет в веб-сокете.
from channels.sessions import channel_session
@channel_session
def ws_connect(message) :
room=message.content ['path']
message.channel_session ['room'] = room
Croup ('chat-%s' % room).add (
message.reply_channel
)
Во время коннекта вы можете получить http сессию и использовать ее в ваших consumer. Как часть negotiation process, установки соединения веб-сокета, передаются cookies пользователя. Соответственно поэтому вы можете получить сессию пользователя, получить объект пользователя, который вы до этого обычно использовали в Django, точно также, как будто работаете с view.
from channels.sessions import http_session_user
@http_session_user
def ws_connect(message) :
message.http_session ['room'] = room
if message.user.username :
…
Message order
Channels позволяет решить очень важную проблему. Если мы устанавливаем соединение с веб-сокетом и сразу делаем send, то это приводит к тому, что два события — WebSocket connect и WebSocket receive — по времени очень близки. Очень вероятно, что consumer для этих веб-сокетов будут выполняться параллельно. Отлаживать это будет очень весело.
Django channels позволяет вводить lock двух видов:
- Легкий lock. С помощью механизма сессий, мы гарантируем, что пока не обработается consumer на получение сообщения, мы не будем обрабатывать никакие message по веб-сокетам. После того, как соединение установлено, порядок произвольный, возможно параллельное выполнение.
- Жесткий lock — в один момент времени выполняется только один consumer конкретного пользователя. Это overhead по синхронизации, поскольку используется медленный движок сессий. Тем не менее такая возможность есть.
from channels.generic import WebsocketConsumer
class MyConsumer(WebsocketConsumer) :
http_user = True
slight_ordering = True
strict_ordering = False
def connection_groups (self, **kwargs) :
return ['chat']
Для того, чтобы это написать, есть такие же декораторы, которые мы видели ранее в http session, channel session. В декларативных consumer можно писать просто атрибуты, как только вы это их напишите, это автоматически применится ко всем методам данного consumer.
Data binding
В свое время прославился Meteor за Data binding.
Открываем два браузера, заходим на одну и ту же страницу, и в одном из них кликаем на скролл-бар. При этом во втором браузере, на этой странице скролл-бар меняет своё значение. Это клево.
class IntegerValueBinding (WebsocketBinding) :
model = IntegerValue
stream = intval'
fields= ['name', 'value']
def group_names (self, instance, action ) :
return ['intval-updates']
def has_permission (self, user, action, pk) :
return True
Django теперь умеет точно так же.
Это реализуется с помощью hook’ов, которые предоставляет Django Signals. Если определен binding для модели, все соединения, которые находятся в группе для данного instance модели будут оповещены о каждом событии. Создали модель, изменили модель, удалили её — всё это будет в оповещение. Оповещение происходит по указанным полям: изменилось значение этого поля — формируется payload, отправляется по веб-сокету. Это удобно.
Важно понимать, что если в нашем примере постоянно кликать скролл-бар, то и постоянно будут идти сообщения и сохраняться модель. Это будет работать до определенной нагрузки, потом всё упрется в базу.
Redis Layer
Поговорим чуть подробнее о том, как устроен самый популярный Channel Layer для продакшена — Redis.
Устроен он неплохо:
- работает с синхронными коннектами на уровне воркеров;
- очень дружественен к Twisted, не тормозит, там, где это особо необходимо, то есть на вашем фронтовом сервере;
- используется MSGPACK для сериализации сообщений внутри Redis, что позволяет уменьшить footprint на каждое сообщение;
- можно распределить нагрузку на несколько экземпляров Redis, она будет автоматически шардиться с помощью алгоритма консистентного хэша. Тем самым, исчезает единая точка отказа.
Канал представляет собой просто список id из Redis. По id находится значение конкретного сообщения. Сделано это для того, чтобы можно было контролировать срок жизни каждого сообщения и канала отдельно. В принципе, это логично.
>> SET "b6dc0dfce" " \x81\xa4text\xachello"
>> RPUSH "websocket.send!sGOpfny" "b6dc0dfce"
>> EXPIRE "b6dc0dfce" "60"
>> EXPIRE "websocket.send!sGOpfny" "61"
Группы реализованы сортированными множествами. Рассылка на группы выполняется внутри Lua-скрипта — это очень быстро.
>> type group:chat
zset
>> ZRANGE group:chat 0 1 WITHSCORES
1) "websocket.send!sGOpfny"
2) "1476199781.8159261"
Problems
Посмотрим, какие проблемы у этого подхода.
Callback Hell
Первая проблема — это заново изобретенный callback hell. Очень важно понимать, что большая часть проблем с каналами, с которыми вы столкнетесь, будут в стиле: в consumer пришли аргументы, которых он не ждал. Откуда они пришли, кто их положил в Redis — все это сомнительная задача на расследование. Отладка распределенных систем вообще для сильных духом. AsyncIO решает эту проблему.
Celery
В интернете пишут, что Django Channels — это замена Celery.
У меня для вас плохие новости — нет, это не так.
В channels:
- нет retry, нельзя отложить выполнение handler;
- нет canvas — есть просто callback. Celery же предоставляет группы, chain, мою любимую chord, которая после параллельного выполнения групп вызывает еще один callback с синхронизацией. Всего этого нет в channels;
- нет задания времени прибытия сообщений, некоторые системы без этого просто невозможно проектировать.
Я вижу будущее, как официальную поддержку использования channels и celery вместе, с минимальными затратами, с минимальными усилиями. Но Django Channels — это не замена Celery.
Django для современного web
Django Channels — это Django для современного web. Это тот самый Django, который мы все привыкли использовать: синхронный, декларативный, с большим количеством батареек. Django Channels — это всего лишь плюс одна батарейка. Всегда надо понимать, где её использовать и стоит ли это делать. Если в проекте Django не нужен, то и Channels там не нужны. Они полезны только в тех проектах, в которых оправдан Django.
Moscow Python Conf++
Профессиональная конференция для Python-разработчиков выходит на новый уровень — 22 и 23 октября 2018 соберем 600 лучших Python-программистов России, представим самые интересные доклады и, конечно же, создадим среду для нетворкинга в лучших традициях сообщества Moscow Python при поддержке команды «Онтико».
Приглашаем специалистов выступить с докладом. Программный комитет уже работает и принимает заявки до 7 сентября.
Для участников ведется онлайн мозговой штурм по программе. В этот документ можно внести недостающие темы или сразу спикеров, выступления которых вам интересны. Документ будет актуализироваться, фактически, вы все время сможете следить за формированием программы.
Вступление | Документация Channels 2.4.0
Добро пожаловать в Channels! Channels подготавливает Django для создания асинхронного кода через синхронное ядро Django, что позволяет проектам Django обрабатывать не только HTTP, но и протоколы, которые также требуют длительных соединений — WebSockets, MQTT, чат-боты, любительское радио и многое другое.
Это достигается при сохранении синхронной и простой в использовании природы Django, что позволяет вам выбирать способ написания кода — синхронно в стиле, подобном представлениям Django, полностью асинхронным или сочетанием обоих. Вдобавок к этому, он обеспечивает интеграцию с системой аутентификации Django, системой сеансов и многим другим, что упрощает расширение вашего HTTP-проекта на другие протоколы.
Он также объединяет эту управляемую событиями архитектуру с уровнем каналов, системой, которая позволяет легко обмениваться данными между процессами и разделять ваш проект на различные процессы.
Если вы еще не установили Channels, вы можете сначала прочитать Установка, чтобы установить его. Это введение не является прямым руководством, но вы должны иметь возможность использовать его, чтобы следовать и вносить изменения в существующий проект Django, если хотите.
Черепахи в порядке убывания¶
Каналы работают по принципу «все черепахи внизу» — у нас есть единое представление о том, что такое «приложение» для каналов, и даже простейший из потребителей (эквивалент представлений Django) является полностью допустимым приложением ASGI, которое можно запускать самостоятельно.
Примечание
ASGI — это название спецификации асинхронного сервера, на котором построен Channels. Как и WSGI, он предназначен для того, чтобы вы могли выбирать между различными серверами и средами, а не быть привязанным к Channels и нашему серверу Daphne. Вы можете узнать больше на http://asgi.readthedocs.io
Каналы дают вам инструменты для написания основных потребителей — отдельных частей, которые могут обрабатывать обмен сообщениями в чате или уведомления — и связывать их вместе с маршрутизацией URL, обнаружением протокола и другими удобными вещами для создания полноценного приложения.
Мы рассматриваем HTTP и существующие представления Django как части большего целого. Традиционные представления Django все еще существуют с Channels и по-прежнему могут использоваться — мы обертываем их в приложение ASGI под названием channel.http.AsgiHandler
— но теперь вы также можете написать собственную обработку длинного опроса HTTP или WebSocket и держать этот код рядом с вашим существующим кодом. URL-маршрутизация, промежуточное ПО — все они являются приложениями ASGI.
Мы считаем, что вам нужна возможность использовать безопасные синхронные методы, такие как представления Django, для большей части кода, но у вас есть возможность перейти к более прямому асинхронному интерфейсу для сложных задач.
Области применения и события¶
Channels и ASGI разделяют входящие соединения на два компонента: scope (область) и серия events (событий).
Scope — это набор сведений об одном входящем соединении — например, путь, с которого был сделан веб-запрос, или исходный IP-адрес WebSocket’а, или пользователь, отправляющий сообщение в чат-бот — и сохраняется в течение всего соединения.
Для HTTP область действия длится всего один запрос. Для WebSocket она длится в течение срока работы сокета (но изменяется, если сокет закрывается и повторно подключается). Для других протоколов это зависит от того, как написана спецификация протокола ASGI; например, вполне вероятно, что протокол чат-бота оставит одну область открытой для всего разговора пользователя с ботом, даже если базовый протокол чата не имеет состояния.
Во время существования этой области действия происходит серия событий. Они представляют пользовательские взаимодействия — например, создание HTTP-запроса или отправка фрейма WebSocket. Ваши каналы или приложения ASGI будут созданы один раз для каждой области, а затем будут переданы потоки событий, происходящих в этой области, чтобы решить, что делать с ними.
Пример с HTTP:
Пользователь делает HTTP-запрос.
Мы открываем новую область типа
http
с подробностями пути запроса, метода, заголовков и т.д.Мы отправляем событие
http.request
с содержимым тела HTTPПриложение Channels или ASGI обрабатывает это и генерирует событие
http.response
для отправки обратно в браузер и закрытия соединения.HTTP-запрос/ответ завершен, а область действия уничтожена.
Пример с чат-ботом:
Пользователь отправляет первое сообщение в чат-бот.
Это открывает область, содержащую имя пользователя, выбранное имя и идентификатор пользователя.
Приложение получает событие
chat.received_message
с текстом события. Он не должен отвечать, но может отправить одно, два или более других сообщений чата обратно какchat.send_message
события, если он этого хочет.Пользователь отправляет больше сообщений чат-боту и генерирует больше событий
chat.received_message
.По истечении времени ожидания или после перезапуска процесса приложения область действия закрывается.
В течение срока действия области действия — будь то чат, HTTP-запрос, соединение с сокетом или что-то еще — у вас будет один экземпляр приложения, обрабатывающий все события из него, и вы также можете сохранять вещи в экземпляре приложения. Вы можете написать «сырое» приложение ASGI, если хотите, но Channels дает вам простую в использовании абстракцию над ними, называемую consumers (потребители).
Что такое потребитель?¶
Потребитель — это основная единица кода Channels. Мы называем это потребителем, поскольку он потребляет события, но вы можете думать о нем как о своем собственном крошечном приложении. Когда приходит запрос или новый сокет, Channels будет следовать таблице маршрутизации — мы рассмотрим это чуть-чуть — найдем подходящего потребителя для этого входящего соединения и запустим его копию.
Это означает, что, в отличие от представлений Django, потребители долгоработающие. Они также могут быть краткосрочными — в конце концов, HTTP-запросы также могут обслуживаться потребителями — но они основаны на идее жить некоторое время (они живут в течение scope, как мы описали выше ).
Основной потребитель выглядит так:
class ChatConsumer(WebsocketConsumer): def connect(self): self.username = "Anonymous" self.accept() self.send(text_data="[Welcome %s!]" % self.username) def receive(self, *, text_data): if text_data.startswith("/name"): self.username = text_data[5:].strip() self.send(text_data="[set your username to %s]" % self.username) else: self.send(text_data=self.username + ": " + text_data) def disconnect(self, message): pass
Каждый отдельный протокол имеет разные виды событий, которые происходят, и каждый тип представлен своим методом. Вы пишете код, который обрабатывает каждое событие, а Channels позаботится о их планировании и параллельном запуске.
Внутри Channels работает в полностью асинхронном цикле событий, и если вы напишите код, как описано выше, он будет вызван в синхронном потоке. Это означает, что вы можете безопасно выполнять блокирующие операции, такие как вызов Django ORM:
class LogConsumer(WebsocketConsumer): def connect(self, message): Log.objects.create( type="connected", client=self.scope["client"], )
Однако, если вам нужен больший контроль и вы хотите работать только с асинхронными функциями, вы можете написать полностью асинхронных потребителей:
class PingConsumer(AsyncConsumer): async def websocket_connect(self, message): await self.send({ "type": "websocket.accept", }) async def websocket_receive(self, message): await asyncio.sleep(1) await self.send({ "type": "websocket.send", "text": "pong", })
Вы можете прочитать больше о потребителях в Потребители (Consumers).
Маршрутизация и несколько протоколов¶
Вы можете объединить несколько Потребителей (которые, помните, сами приложения ASGI) в одно большее приложение, которое представляет ваш проект с использованием маршрутизации:
application = URLRouter([ url(r"^chat/admin/$", AdminChatConsumer), url(r"^chat/$", PublicChatConsumer), ])
Channels не просто строятся вокруг мира HTTP и WebSockets — они также позволяют вам встроить любой протокол в среду Django, создав сервер, который отображает эти протоколы в похожий набор событий.chat/$», PublicChatConsumer),
]),
«telegram»: ChattyBotConsumer,
})
Цель Channels — позволить вам создавать свои проекты Django для работы с любым протоколом или транспортом, с которыми вы можете столкнуться в современной сети, и в то же время позволяя вам работать со знакомыми компонентами и стилем кодирования, к которым вы привыкли.
Для получения дополнительной информации о маршрутизации протокола см. Маршрутизация.
Межпроцессное взаимодействие¶
Подобно стандартному серверу WSGI, код приложения, который обрабатывает события протокола, выполняется внутри самого процесса сервера — например, код обработки WebSocket выполняется внутри процесса сервера WebSocket.
Каждый сокет или соединение с вашим общим приложением обрабатывается экземпляром приложения на одном из этих серверов. Они получают вызовы и могут отправлять данные обратно клиенту напрямую.
Однако, когда вы создаете более сложные прикладные системы, вы начинаете нуждаться в связи между различными экземплярами приложений — например, если вы создаете чат-комнату, когда один экземпляр приложения получает входящее сообщение, ему необходимо распространить его среди любых других экземпляры, которые представляют людей в чате.
Вы можете сделать это путем опроса базы данных, но Channels вводит идею уровня канала, низкоуровневой абстракции вокруг набора транспортов, которые позволяют отправлять информацию между различными процессами. Каждый экземпляр приложения имеет уникальное имя канала и может присоединяться к группам, что позволяет осуществлять как двухточечный, так и широковещательный обмен сообщениями.
Примечание
Слои каналов являются необязательной частью Channels и могут быть отключены, если вы хотите (установив для параметра CHANNEL_LAYERS
пустое значение).
(вставьте пример кросс-процесса здесь)
Вы также можете отправлять сообщения выделенному процессу, который прослушивает свое собственное фиксированное имя канала:
# In a consumer self.channel_layer.send( "myproject.thumbnail_notifications", { "type": "thumbnail.generate", "id": 90902949, }, )
Вы можете прочитать больше о слоях каналов в Слои каналов.front(end)/$», consumers.AsyncChatConsumer),
])
),
})
Для получения дополнительной информации смотрите Sessions и Authentication.
Введение в Django Channels — WebSofter
Добро пожаловать в Django Channels! Каналы меняют Django, чтобы сплести под ним асинхронный код через синхронное ядро, позволяя проектам на Django обрабатывать не только HTTP, но и протоколы, которые требуют подключения соединения на длительное время — WebSockets, MQTT, чатботы, пользовательское радио и многое другое.
Это происходит в то время, когда Django остается синхронным и простым в использовании, позволяя нам выбрать то, как написать наш код: синхронно в стиле Django Views или смешав с асинхронным. Помимо этого он предоставляет интеграцию с такими модулями Django, как система авторизации, система сессий и др., что упрощает, как никогда, расширение нашего проекта, имея только HTTP-протокол, до других протоколов.
Он также связывает эту событийную архитектуру со слоем каналов. Получается система которая позволяет вам легко обмениваться данными между процессами и отделив наш проект от разных процессов.
Черепахи всегда идут вниз
Channels оперируют по принципу «первый вошел — первым вышел». У нас есть единая идея или картина того, что есть приложения в виде Channels и даже самый простой Consumer(эквивалент Views в Django) является полными работоспособными приложениями ASGI, которые вы можете запускать самостоятельно.
Channels дают вам инструмент, чтобы написать базовые consumers — индивидуальные части, которые могу обрабатывать сообщения чата или уведомления — и связывать их вместе с машрутизацией URL, обнаружением протокола и другими удобными вещами, чтобы создавать полноценное приложением.
Мы рассматриваем HTTP и существующие Django Views, как части большого целого. Традиционно Django Views все также существуют с Channels и все также используемы — мы оборачиваем их в ASGI приложение, называемое channels.http.AsgiHandler
. Теперь вы можете написать свой long-polling HTTP — обработчик или приемники WebSocket и бок о бок с существующим кодом Django. URL — маршрутизация и промежуточные модули — это все простые приложения ASGI.
Мы убеждены в том, что вы хотели сохранить возможность использования синхронной техники Django для большинства кода, но иметь опцию прямого асинхронного интерфейса для сложных задач.
Области и события
Channels и ASGI разделяют входящее соединение на 2 компонента: scope(областью) и серия events(событий).
Scope — это набор подробностей о одном входящем соединении — например, путь, по которому веб-запрос был сделан или исходящий IP-адрес WebSocket, или пользовательский обмен сообщениями с чатом — и это сохраняется в течение всего соединения.
Для HTTP протокола scope — это просто один произошедший запрос. Для WebSocket он является жизненным циклом сокета (но изменяется, если сокет закрывается и повторно соединяется).
Для других протоколов это зависит от того, как написана спецификация протокола ASGI. Например, вполне вероятно, что протокол chatbot будет держать одну область открытой для всей беседы пользователя с ботом, даже если базовый протокол чата не имеет статуса.
В течение всего срока действия этой Scope(области) происходит ряд Events(событий). Они представляют пользовательское взаимодействие — создание HTTP — запроса, WebSocket — фрейм и т.д.
Наши каналы или приложения ASGI будут создаваться один раз для каждой Scope(области), а затем будут передаваться поток Events(событий), происходящих в этой области, чтобы решить, что делать.
Пример с HTTP:
- пользователь создает запрос HTTP;
- мы открываем новую Scope(область) типа
http
с деталями пути запроса, метода, заголовков, и т.д. - мы отправляем событие
http.request
с контентом тела HTTP; - приложения Channels или ASG обрабатывают это и генерируют событие
http.response
для обратного ответа браузеру и закрывает соединение; - HTTP — request(запрос)/response(ответ) заканчиваются и Scope(область) уничтожается.
Пример с чатом:
- пользователь отправляет первое сообщение чату;
- открывается Scope(область), которая содержит пользовательский ник, выбранного имени и пользовательский ID;
- программа получает Events(событие)
chat.received_message
с текстом события. Оно не имеет ответ, должен быть один, два или больше в качестве Events(события)chat.send_message
если есть в этом потребность; - чем больше пользователь отправляет в чат сообщений тем больше генерируются Events(события)
<span chat.received_message
- после таймаута или когда процесс программы возобновляется Scope(область) закрывается.
В течение срока действия Scope(области) — будь то чат, HTTP-запрос, сокет-соединение или что-то еще — у вас будет один экземпляр приложения, обрабатывающий все события из него, и вы также сможете перенести вещи на экземпляр приложения. Вы можете выбрать, если хотите, сырое приложение ASGI, но Channels(каналы) дают вам удобную абстракцию над ними, называемую Consumers(потребителями).
Что такое Consumer?
Consumer — это потребитель событий и является базовой единицей в коде Channels. Мы называем его Consumer(потребитель), так как он потребляет события, но вы можете подумать об этом, как об ихнем маленьком приложении. Когда приходит запрос или новый сокет, Chanells(каналы) будут следовать их таблице маршрутизации — мы рассмотрим немного этот момент — находят там правильного/нужного Consumer(потребителя) для этих входящих соединений и запустит на выполнение его копию.
Это значит, что в отличии от Views(видов) Django, Consumers(потребители) рассчитаны на запуск в долгое время работы. Они также могут быть запущены на короткое время — ведь, HTTP запросы могут быть обработаны и Consumers(потребителями) — но они построены вокруг идеи жизни в короткий промежуток( они живут в течении Scope(области), которых мы описали выше).
Ниже представлен базовый Consumer(потребитель):
class ChatConsumer(WebsocketConsumer): def connect(self): self.username = "Anonymous" self.accept() self.send(text_data="[Welcome %s!]" % self.username) def receive(self, *, text_data): if text_data.startswith("/name"): self.username = text_data[5:].strip() self.send(text_data="[set your username to %s]" % self.username) else: self.send(text_data=self.username + ": " + text_data) def disconnect(self, message): pass<span data-mce-type="bookmark" data-mce-fragment="1"></span>
Каждый из разных протоколов имеет разный способ появления событий и каждый тип реализован различным методом. Вы пишете код обработки каждого события и Channels(каналы) позаботятся об их расписании выполнения их всех в параллельном режиме.
Внизу, Channels(каналы) запущены в полном асинхронном событийном цикле и если вы пишете код, похожий выше, то он будет вызван в синхронном потоке. Это означает, что вы безопасно выполнять блокирующие операции, такие как вызов Django ORM:
class LogConsumer(WebsocketConsumer): def connect(self, message): Log.objects.create( type="connected", client=self.scope["client"], )<span data-mce-type="bookmark" data-mce-fragment="1"></span>
Однако, если вы хотите больше контроля, и вы готовы работать только в асинхронных функциях, вы можете написать полностью асинхронных Consumers(потребителей):
class PingConsumer(AsyncConsumer): async def websocket_connect(self, message): await self.chat/$", PublicChatConsumer), ])
Channels(каналы) не только построены вокруг мира HTTP и WebSockets, но также позволяют нам построить любой протокол внутри Django среды, создавая сервер, который отображает эти протоколы в один и тот же набор событий. К примеру, вы можете построить бот чата с стиле:
class ChattyBotConsumer(SyncConsumer): def telegram_message(self, message): """ Simple echo handler for telegram messages in any chat. """ self.send({ "type": "telegram.message", "text": "You said: %s" % message["text"], })
И затем используем другой маршрутизатор, чтобы один проект мог обслуживать как WebSockets, так и чат-запросы:
application = ProtocolTypeRouter({ "websocket": URLRouter([ url(r"^chat/admin/$", AdminChatConsumer), url(r"^chat/$", PublicChatConsumer), ]), "telegram": ChattyBotConsumer, })
Цель Channels(каналов)- это позволить вам построить ваши собственные Django проекты для работы через любой протокол или транспорт, с которыми вы можете столкнуться в современном WEB, позволяя вам работать со знакомыми компонентами и стилем кодирования, к которому вы привыкли.
Больше информации про протоколы и маршрутизацию можно узнать по ссылке.
Кросспроцессная коммуникация
Как стандартный WSGI-сервер, ваш код приложения, обрабатывающий события протокола, выполняется внутри самого процесса сервера — например, код обработки WebSocket выполняется внутри вашего сервера WebSocket.
Каждый сокет или соединение с вашим общим приложением обрабатывается экземпляром приложения на одном из этих серверов. Они получают вызов и могут напрямую отправлять данные клиенту.
Однако при создании более сложных прикладных систем вам необходимо установить связь между различными экземплярами приложения — например, если вы создаете чат-комнату, когда один экземпляр приложения получает входящее сообщение, он должен распространять его на любые другие экземпляры, которые представляют людей в чате.
Вы можете сделать запрос к базе данных, но Channels(каналы) вносят идею канального слоя, низкоуровневую абстракцию вокруг набора транспортов, которые позволяют вам отправлять информацию между различными процессами. Каждая сущность приложения имеет уникальное канальное имя, и может присоединяться к группам, позволяя отправлять сообщения между точками или широковещать на целую группу.
Канальный слой — это дополнение к Channels(каналам) и может быть запрещен, если он не нужен. для этого нужно установить для CHANNEL_LAYERS пустое значение.
Вы также можете отправлять сообщения в выделенный процесс, который прослушивает самостоятельно, фиксированное имя канала:
# In a consumer self.channel_layer.send( "myproject.thumbnail_notifications", { "type": "thumbnail.generate", "id": 90902949, }, )
Вы можете почитать еще больше про канальный слой на офф-сайте Channel Layers.
Интеграция с Django
Channels(каналы) поставляются с простой поддержкой обычных функций Django, таких как сеансы и аутентификация.front(end)/$», consumers.AsyncChatConsumer),
])
),
})
Для дополнительной информации можно почитать Sessions и Authentication.
Как работает библиотека Django channels будут примеры
Автор статьи: admin
В этой статье мы рассмотрим основы работы с библиотекой channels для Python Django, которая работает с WebSocket и конечно будут примеры.
Также если вас интересует работа с WebSocket, то прочитайте эти две статьи:
Как установить Django channels:
Первым делом как всегда нужно установить библиотеку, для этого как обычно на Python используем PIP.
python -m pip install -U channels |
Теперь в список INSTALLED_APPS
который находится в «settings.py», добавьте надпись channels
.
INSTALLED_APPS = ( ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.sites’, … ‘channels’, ) |
Также в настройки нужно добавить не большую переменную, которая будет обозначать где находится роутингом.
ASGI_APPLICATION = «myproject.routing.application» |
Также создайте вашей главной папке проекта файл «routing.py», эта и есть настройка для WebSocket, туда мы и направляли переменную выше, вот что там должно быть.
from channels.routing import ProtocolTypeRouter
application = ProtocolTypeRouter({ # Пока пусто (http-> django views добавлены по умолчанию) }) |
На этом установка заканчивается.
Базовая работа с Django channels:
Теперь перейдём к самой работе с этой библиотекой, но тут стоит сказать, что это скорее не большой перевод обучения с официальной документации.
В качестве примера мы будем сделаем не большой чат, и весь код будет хранится в приложение «chat», но я не буду тут показывать все шаблоны и весь код чата, а только ту часть за которую отвечает channels, сам чат будет в одной из будущих статей.
Если у вас еще нет проекта и приложения Django, вам нужно будет его создать, для этого можете посмотреть статью «Быстрый старт на Django».
После того как всё создали, добавляем в папку с настройками файл «routing.py», в нём пишем вот это.
from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter import chat.routing
application = ProtocolTypeRouter({ ‘websocket’: AuthMiddlewareStack( URLRouter( chat.routing.websocket_urlpatterns ) ), }) |
Тут мы по сути добавляем файл «routing.py», который находится в папке приложения, если кратко говоря, то это аналог файла «urls.py».
Теперь создадим этот файл в приложение «chat», там будет весь основной код.
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [ re_path(r’ws/chat/(?P<room_name>\w+)/$’, consumers.ChatConsumer), ] |
То есть это аналог «urls.py», в нём назначается путь куда нужно отправлять WS для работы в Django 3 WebSocket, и какой класс использовать для их обработки.
Сам же класс будем писать во файле «consumers.py», который находится в папке с приложением, вот что будет внутри его.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import json from channels.generic.websocket import WebsocketConsumer
class ChatConsumer(WebsocketConsumer): def connect(self): self.accept()
def disconnect(self, close_code): pass
def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json[‘message’]
self.send(text_data=json.dumps({ ‘message’: message })) |
То есть в этом файле, в начале мы подключаем все нужные компоненты, самое главное это json, так как, отправлять клиенту мы будем именно json данные.
В классе ChatConsumer
, который мы унаследовали с WebsocketConsumer
, первый же метод у нас connect()
, и как понятно из названию, он что то делает во время подключения к серверу через WS, второй метод disconnect()
описывает действия при отключения от сервера, последние это receive()
, как правела в ней отправляются данные всем пользователям, для отправки используется метод send()
.
Это пожалуй самое главное что стоит знать про эту библиотеку, в остальном просто нужно внутри этих методов писать нужный вам код, вот и всё.
Примеры как можно использовать:
Ну самое главное для чего можно использовать эту библиотеку, так это для создания чатов или даже социальных сетей.
Второе, это для браузерных многопользовательских игр, тоже очень хорошо для этого подойдёт.
Вывод:
В этой статье вы прочитали как работать библиотекой Django channels, где были примеры, как можно это использовать, если вас она заинтересовала, то посмотрите документацию.
Подписываетесь на соц-сети:
Оценка:
(Пока оценок нет)
Загрузка…
Чат на Python Django c использованием channels
Автор статьи: admin
В этой статье мы разберём как сделать чат на Python Django, тут будет только самое базовое, что нужно знать про это.
Примечание:
Также крайне рекомендуется перед прочтением, прочитать статью «Django библиотека channels для WebSocket», так как в этой мы не будем показывать настройку и основы работы с библиотекой channels а её мы и будем использовать для реализации WebSocket на Django.
Настройка проекта:
Первым делом нужно всё настроить, для этого нужно создать виртуальное окружение, тут можете посмотреть статью про это «Создание Virtual Environments Python».
После того как сделали виртуальное окружение и создали проект на Django, можете скачать саму библиотеку channels и ещё один не большой компонент, для того чтобы она могла работать Redis.
python3 -m pip install channels_redis |
В Redis будет временно сохранятся наши сообщения, для этого она и нужна.
Примечание:
Redis – БД типа NoSQL, которая хранит данные в ОЗУ, доступ к которым осуществляется по ключу если хотите узнать подробнее, то зайдите по ссылки.
Теперь не много изменим файл settings.py, добавим не большой список.
CHANNEL_LAYERS = { ‘default’: { ‘BACKEND’: ‘channels_redis.core.RedisChannelLayer’, ‘CONFIG’: { «hosts»: [(‘127.0.0.1’, 6379)], }, }, } |
Тут мы настроили работу с Redis, остальные настройки в дрогой статье, конкретно про библиотеку.
Создание проекта:
Теперь перейдём к созданию проекта, тут первым делом во файле «models.py», создадим модель для сообщений, примерно так:
from django.db import models
class Message(models.Model): id = models.AutoField(primary_key=True, unique=True) text = models.CharField(max_length=255) |
Тут не чего такое нет, модель имеет только идентификатор сообщения и текст, дальше нужно сделать миграции, используете эти команды:
python3 manage.py makemigrations python3 manage.py migrate |
Дальше создадим файл «routing.py», это аналог «urls.py», только для WebSocket.
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [ re_path(r’ws/chat/’, consumers.ChatConsumer), ] |
Здесь мы назначаем только один путь, где будут обрабатывается все WS запросы, следующие что нужно сделать, это создать файл «consumers.py» и в нём делаем класс ChatConsumer
, он будет обрабатывать все WS запросы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | # Импорт для работы с JSON import json # Импорт для асинхронного программирования from channels.generic.websocket import AsyncWebsocketConsumer # Импорт для работы с БД в асинхронном режиме from channels.db import database_sync_to_async # Импорт модели сообщений from .models import Message
# Класс ChatConsumer class ChatConsumer(AsyncWebsocketConsumer):
# Метод подключения к WS async def connect(self): # Назначим пользователя в комнату self.room_group_name = «1» # Добавляем новую комнату await self.channel_layer.group_add( self.room_group_name, self.channel_name ) # Принимаем подключаем await self.accept()
# Метод для отключения пользователя async def disconnect(self, close_code): # Отключаем пользователя await self.channel_layer.group_discard( self.room_group_name, self.channel_name )
# Декоратор для работы с БД в асинхронном режиме @database_sync_to_async # Функция для создания нового сообщения в БД def new_message(self, message): # Создаём сообщение в БД Message.objects.create(text=message)
# Принимаем сообщение от пользователя async def receive(self, text_data=None, bytes_data=None): # Форматируем сообщение из JSON text_data_json = json.loads(text_data) # Получаем текст сообщения message = text_data_json[‘message’]
# Добавляем сообщение в БД await self.new_message(message=message)
# Отправляем сообщение await self.channel_layer.group_send( self.room_group_name, { ‘type’: ‘chat_message’, ‘message’: message, } )
# Метод для отправки сообщения клиентам async def chat_message(self, event): # Получаем сообщение от receive message = event[‘message’] # Отправляем сообщение клиентам await self.send(text_data=json.dumps({ ‘message’: message, }, ensure_ascii=False)) |
Теперь разберём файл, в начале мы подключаем все нужные компоненты, их особо не буду объяснять, так как в комментариях всё понятно написано.
Внутри класса первым делом идёт метод который отслеживает подключения пользователя в комнату, то есть, если он будет писать туда сообщения, то его получат все кто находится в этой комнате, можете заметить, что я просто назначаю номер комнаты один, это сделано для того, чтобы все сидели в одном чате, а так вы можете в URL передавать номер комнаты и назначать пользователя туда, когда он подключается.
Потом пишем метод disconnect()
, он срабатывает когда клиент отключается от WebSocket, создаём функцию с декоратором @database_sync_to_async
, он позволяет работать с БД асинхронная, это нужно, потому что Django по умолчанию работает синхронна, если этого не сделать, то будет ошибка, создаём метод для сохранения сообщения в БД.
Дальше пишем метод для принятии сообщений от клиента, и так как мы получаем JSON массив, мы превращаем его в обычный Python список, получаем сообщение, используем метод new_message()
и отправляем данные методу для отправки клиентам сообщений, в методе chat_message()
, отправляем сообщение всем подключённым клиентам.
На этом код для WebSockrt на сервере закончился, теперь создадим JavaScript скрипт для работы с этим.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | let chat = document.querySelector(«#chat») let input = document.querySelector(«#message-input») let btnSubmit = document.querySelector(«#btn-submit»)
const webSocket = new WebSocket(‘ws://’ + window.location.host + ‘/ws/chat/’);
webSocket.onmessage = function(e) { const data = JSON.parse(e.data); chat.innerHTML += ‘<div>’ + data.message + ‘</div>’ };
btnSubmit.addEventListener(«click», () => { message = input.value; webSocket.send(JSON.stringify({ ‘message’: message })); input.value = »; }) |
Этот скрипт очень простой и короткий, поэтому я его не буду объяснять, да и в целом это статья про Python django, вы уже должны хорошо знать JS.
Теперь надо создать HTML шаблон, сделаем это в папке «templates», в главном приложение, где вся логика, назовём файл «room.html».
<!DOCTYPE html> <html> <head> <meta charset=»utf-8″/> <title>Chat Room</title> {% load static %} </head> <body> <input type=»text» size=»100″><br> <input type=»button» value=»Send»> <div>
</div> <script type=»text/javascript» src=»{% static ‘script.js’ %}»></script> </body> </html> |
Добавим рендаринг этой страницы и прописать URL до неё.
from django.shortcuts import render
def chat(request): return render(request, ‘room.html’) |
Добавляем URL:
from django.contrib import admin from django.urls import path
from main import views |
10 пакетов Django, которые вы должны знать обязательно!
Вы вскоре собираетесь начать проект на Django? Вероятно, вам любопытно, есть ли какие-нибудь полезные пакеты, которые могли бы вам помочь. Мы составили список самых нужных пакетов Django. Приложенные короткие описания и ссылки на сами пакеты помогут вам начать работать с ними как можно скорее. Все пакеты, которые мы выбрали, поддерживают Python 3 и находятся в стабильной версии.
Готовы ли вы исследовать мир пакетов Django? Погружаемся!
Но прежде всего — является ли Django библиотекой?
В мире инструментов Python легко заблудиться, ведь сегодня в его экосистеме доступно так много новых фреймворков и библиотек. Но все же стоит уточнить: Django — это не библиотека, а фреймворк.
Django — бесплатный высокоуровневый фреймворк с открытым кодом для разработки веб-приложений на Python, позволяющий ускорить разработку и создавать более чистый дизайн. Он был создан избавить программистов от лишних хлопот, связанных с разработкой веб-приложений, без необходимости заново создавать велосипед. А значит – писать меньше строк кода!
Благодаря своей невероятной скорости, высокой безопасности, гибкости и масштабируемости Django быстро набирает обороты и сегодня является одной из самых популярных технологий для веб-разработки на Python.
Разница между модулями и пакетами в Django?
Модули и пакеты иногда путают друг с другом, поэтому, прежде чем перейти к нашему списку, давайте рассмотрим разницу между ними.
Модуль — это один файл Python, а пакет — это каталог таких модулей. В отличие от простых директорий, содержащих скрипты Python, пакет содержит еще и дополнительный файл — _init_.py.
Лучшие пакеты Django
Примечание: поскольку есть несколько веских причин, по которым написание фронтенда при помощи Django является не лучшей идеей, мы решили сосредоточиться на пакетах для бэкенд-проектов.
1. Sentry-sdk
Описание:
Sentry-sdk — обязательный инструмент для любого проекта Django, так как он позволяет нам быть в курсе всех связанных с кодом проблем в приложении.
Во-первых, пакет немедленно предупреждает нас обо всех ранее неучтенных исключениях, которые проявились в продакшене.
Во-вторых, мы можем сами обнаружить проблему и отправить информацию о ней в sentry, добавив дополнительные данные.
Самостоятельное создание системы предупреждений это, безусловно, нетривиальная задача, а отладка без подобных логов практически невозможна.
Чем полезен этот пакет:
- Ситуация, когда сервер выдает ошибку, и вам не нужно разбираться в чем именно была проблема.
Особенности:
- Sentry будет ловить любые неучтенные исключения и немедленно отправлять нам полную информацию о проблеме.
- Когда код работает правильно, sentry полезен в качестве системы оповещения, в которую мы можем отправлять ошибки или логи о любых ненормальных событиях.
2. Django REST framework
Описание:
Если вы хотите написать API архитектурных ограничений REST, Django REST framework сделает это за вас. Также он автоматически сгенерирует правильную документацию, поддерживающую реализацию пакета в проектах.
Чем полезен этот пакет:
Нет необходимости самостоятельно писать REST API.
Особенности:
- Для создания конечных точек REST API для ваших моделей потребуется всего несколько строк кода.
- Автоматически сгенерированная документация — отличная вещь, потому что она всегда меняется вместе с конечными точками.
3. Django-extensions
Описание:
Django-extensions — набор инструментов, которые помогут вам в вашей повседневной работе. JSONField был впервые представлен в этом пакете еще до того, как он официально стал частью Django 1.9 (для PostgreSQL, конечно).
Чем полезен этот пакет:
- Добавляет кучу случайных полезных функций.
Особенности:
- Команда shell_plus запускает оболочку Django с предварительно загруженными файлами, классами и моделями, которые ускоряют любую работу с этой оболочкой.
- Дополнительное поле AutoSlugField генерирует уникальный slug для заданных данных.
- Дополнительные поля CreationDateTimeField и ModificationDateTimeField сохраняют в базе данных первое и последнее изменение даты и времени соответственно.
4. Django-rest-framework-jwt
Описание:
При использовании DRF для некоторых проектов необходимо добавить JWT-аутентификацию. Для этого и предназначен этот пакет.
Чем полезен этот пакет:
- Вам не придется самостоятельно реализовывать токен аутентификации для API и управлять им.
Особенности:
- Предоставляет все необходимое для JWT-аутентификации: конечную точку входа и класс Django auth.
5. Django-rest-swagger
Описание:
DRF предлагает автоматически сгенерированную документацию, а этот пакет делает документацию для ваших проектов более красивой.
Чем полезен этот пакет:
- Бесплатное создание красивой документации на основе API DRF.
Особенности:
- Хорошая автоматическая документация для ваших конечных точек.
- Пользовательские конечные точки легко документировать вручную.
6. Easy-thumbnails
Описание:
Любой веб-сервис, позволяющий загружать и просматривать фотографии, нуждается в возможности создания миниатюр. Easy-thumbnails обеспечивает это.
Чем полезен этот пакет:
- Все серверы, на которых размещены изображения, нуждаются в миниатюрах, и этот пакет является решением проблемы.
Особенности:
- Динамическое создание миниатюр на основе загруженного изображения.
7. Django-simple-history
Описание:
Django-simple-history хранит историю изменения записей. Если клиент имеет доступ к базе данных через панель администратора, он может что-то изменить по ошибке и захотеть отменить это изменение или, скажем, вернуться…
Чем полезен этот пакет:
- Сохраняет историю измененных строк в базе данных.
Особенности:
- Отслеживает все изменения модели, в то время как настройка не занимает много времени.
8. Django-adminactions
Описание:
Простой экспорт данных в известные типы файлов, такие как CSV или XLS. Вы также можете экспортировать данные в качестве приспособления на тестовый сервер, и это приспособление может содержать внешние ключи. Django-adminactions также позволяет генерировать графики в панели администратора.
Чем полезен этот пакет:
- Добавляет некоторые функции, которые могут пригодиться на ранних стадиях разработки приложения.
Особенности:
- Экспорт данных из админки в XSL, CSV, Fixture.
9. Django-model-utils
Описание:
Django-model-utils — набор полезных утилит для моделей Django.
Чем полезен этот пакет:
- Добавляет некоторые модели и поля, которые помогают с некоторыми распространенными проблемами.
Особенности:
- Поле MonitorField, которое является полем DateTime и обновляется до now() каждый раз, когда ему требуется обновление.
10. Django-storages
Описание:
Django-storages позволяет использовать любые облачные сервисы хранения данных в качестве хранилища файлов по умолчанию. Это становится важным, когда вы хотите пересылать в другое хранилище все, что отправляют нам пользователи.
Например, пользователь может отправить нам много существенных данных, таких как фотографии, фильмы или даже резервные копии. Чтобы сделать эти данные доступными для него, не замедляя работу приложения, рекомендуется использовать облачные решения для хранения данных, такие как Google Cloud.
Чем полезен этот пакет:
- Использование практически любого удаленного хранилища в качестве хранилища файлов фреймворка по умолчанию.
Особенности:
- Позволяет использовать s3 или Google storage в качестве хранилища файлов по умолчанию.
Дополнительно: что такое Django Allauth?
Приведенный выше список включает в себя самые лучшие пакеты Django, доступные в его богатой экосистеме. Но есть еще один пакет, который, как нам кажется, должен стать частью этого списка в качестве дополнительного пункта..
Django-allauth
Django-allauth предлагает интегрированный набор приложений Django, которые решают такие проблемы, как аутентификация, регистрация, управление учетными записями, а также аутентификация сторонних учетных записей (например, аккаунтов в социальных сетях).
Почему мы включили его сюда:
Большинство существующих приложений Django, которые решают проблему аутентификации в соцсетях, фокусируются только на этом функционале. Для поддержки аутентификации через локальный аккаунт, разработчикам, как правило, приходится интегрировать другое приложение. При таком подходе локальная аутентификация и аутентификация в соцсетях аутентификация отделяются друг от друга, и их интеграция становится сложной задачей.
Django-allauth был создан, чтобы заполнить этот пробел. Он предлагает пользователям полностью интегрированное приложение для аутентификации, как локальной, так и в соцсетях.
Вывод
Python стал важным языком программирования для проектов веб-разработки по многим причинам. Одной из них является богатая экосистема библиотек, фреймворков и пакетов, помогающих разработчикам ускорить процесс создания приложений, лучше управлять ими и сосредоточиться на самом важном (например, архитектуре приложений или переводе бизнес-логики).
Мы надеемся, что этот список пакетов поможет вам, когда вы будете создавать свое следующее веб-приложение с использованием Python и Django. Если вы хотите узнать больше о пакетах Django, загляните на этот полезный сайт: https://djangopackages.org/.
Кроме того, не забудьте просмотреть и другие статьи на нашем сайте: в них вы найдете еще больше полезных советов по созданию приложений на Django!
Как развернуть каналы Django в рабочей среде
В этой статье мы увидим, как развернуть каналы django в производственной среде и как мы можем масштабировать его для обработки большей нагрузки. Мы будем использовать nginx в качестве прокси-сервера, daphne в качестве сервера ASGI, gunicorn в качестве сервера WSGI и redis для внутреннего канала.
Дафна может обслуживать HTTP-запросы, а также запросы WebSocket. Для стабильности и производительности мы будем использовать uwsgi / gunicorn для обслуживания HTTP-запросов и daphne для обслуживания запросов веб-сокетов.
Мы будем использовать systemd для создания процессов и управления ими вместо того, чтобы полагаться на сторонних менеджеров процессов, таких как супервизор или цирк. Мы будем использовать ansible для управления развертыванием. Если вы не хотите использовать ansible, вы можете просто заменить переменные шаблона в следующих файлах фактическими значениями.
Настройка Nginx
Nginx будет маршрутизировать запросы к серверу WSGI и серверу ASGI на основе URL-адреса. Вот конфигурация nginx для сервера.
сервер { слушать {{server_name}}: 80; имя_сервера {{server_name}} www.{{ название сервера }}; возврат 301 https: //avilpage.com$request_uri; } server { слушать {{server_name}}: 443 ssl; имя_сервера {{server_name}} www. {{server_name}}; ssl_certificate /root/certs/avilpage.com.chain.crt; ssl_certificate_key /root/certs/avilpage.com.key; access_log /var/log/nginx/avilpage.com.access.log; error_log /var/log/nginx/avilpage.com.error.log; место расположения / { proxy_pass http://0.0.0.0:8000; proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; proxy_set_header Хост $ http_host; proxy_set_header X-Real-IP $ remote_addr; proxy_redirect выключен; } location / ws / { proxy_pass http: // 0.0,0.0: 9000; proxy_http_version 1.1; proxy_read_timeout 86400; proxy_redirect выключен; proxy_set_header Обновить $ http_upgrade; proxy_set_header Соединение «апгрейд»; proxy_set_hea
Django REST Framework и каналы
Назад, Том Кристи, создатель Django REST Framework (DRF), сказал:
Я думаю, что больше всего людям не хватает банкомата, вероятно, просто
отсутствие руководств или сообщений в блогах по совместному использованию каналов и инфраструктуры REST.— Том Кристи
Я понял, что это именно , над чем мы работаем в последнее время в OddBird, поэтому
Я думал, что напишу незавершенный отчет. Я совсем не уверен
что это лучший способ, но он, похоже, пока работает.
Основы
Первый, DRF. Я кратко коснусь этого, предполагая, что вы
уже знаком с ней. Если нет, их собственная документация
лучше, чем любое резюме, которое я могу здесь предложить. Мы сделали красивый
традиционный RESTful API с ним, сохраняя конечные точки плоскими, с
минимальная серверная логика, в основном инкапсулированная в сериализаторах.
Как раз мы находимся на одной странице, конечные точки выглядят примерно так:
GET / api / foo
POST / api / foo
ПОЛУЧИТЬ / api / foo /: идентификатор
PUT / api / foo /: идентификатор
УДАЛИТЬ / api / foo /: идентификатор
И код организован так:
foo /
__init__.py
models.py
serializers.py
urls.py
views.py
тесты /
__init__.py
models.py
serializers.py
views.py
(Для того, что я не распространяю как библиотеку, я предпочитаю оставлять
тесты параллельно коду и в одном дереве; Я считаю, что это делает
легче работать с тестами, которые относятся к коду, который вы
трогательно, когда вы работаете над этим.Если я пишу библиотеку, я рутирую тесты
в другом дереве, но со структурой, параллельной коду; этот
упрощает их исключение при установке.)
Внутри этих файлов у нас в основном простое декларативное использование DRF. следить
их руководство, если вы хотите это настроить.
Мы используем pytest для всех наших тестов Python и требуемый 100% тест
покрытие. Из-за этого мы не можем просто пропустить что-то «слишком сложное».
для тестирования, поэтому я немного расскажу о нашей настройке тестирования позже.
Сейчас каналы. Я не встречал людей, знакомых с ним так хорошо, как с
DRF, поэтому я еще немного расскажу, как мы его настраиваем. Это не слишком
плохо, но он отличается от обычного Django.
Во-первых, вам нужно перейти от использования WSGI к ASGI, что «в основном
WSGI, но асинхронный ». Это означает изменение вашего серверного процесса с Gunicorn
(или что бы вы ни использовали) на что-то вроде Дафны, изменив
Модуль project.wsgi
в project.asgi
(как описано в разделе Каналы
docs), добавив модуль маршрутизации
и модуль потребителей
, и
соответствующим образом изменив настройки.
На данном этапе у вас еще не будет ничего из потребителей
, ни много
маршрутизация
. маршрутизация
может выглядеть так:
из каналов. Импорт маршрутизации ProtocolTypeRouter application = ProtocolTypeRouter ({
})
Да, это в основном пустой ProtocolTypeRouter
. Мы только первые
убедившись, что мы ничего не сломаем с переходом на ASGI, и
что ProtocolTypeRouter
правильно связывает HTTP с Django.
Когда все будет готово, и вы убедитесь, что все в порядке
Работая, можно начинать добавлять в проводку для WebSockets.
Веб-сокеты, потребители и группы
Давайте поговорим немного об архитектуре, прежде чем погрузиться в реализацию.
Как мы его используем, каналы в первую очередь побуждают WebSockets подталкивать
уведомления клиента. Мы решили упростить работу клиента,
имея одну конечную точку, которую он может вызвать для подписки на любой объект, который он
хочет, используя полезную нагрузку, которую он отправляет для проверки и настройки этого
подписка.Таким образом, клиент отправляет следующие данные в
wss: //server.domain/ws/notifications/
:
{
"модель": "app.label",
"id": "123ABC"
}
Модель похожа на foo.Foo
с использованием синтаксиса apps.get_model
ожидает. Идентификатор — это HashID рассматриваемого экземпляра модели. (Мы
использовать HashID везде, где мы можем, чтобы избежать утечки информации через
последовательные идентификационные номера.)
Затем сервер решит, может ли запрашивающий пользователь подписаться на это
model и начните отправлять им обновления через этот WebSocket, если это так.
На стороне сервера у нас есть объект Consumer
, который обрабатывает
набор событий WebSocket и, при необходимости, добавляет конкретный
подключение сокета к именованной группе
. В другом месте логики сервера мы
отправлять события в эту группу
при изменении модели, и все подписываются
сокеты получат сериализацию модели с изменениями.
(поскольку мы используем React в качестве интерфейса для этого проекта, мы также
отправка значения, которое сопоставляется с именами событий Redux, которые мы
использование, но такая тесная связь может не соответствовать вашим потребностям.)
Хорошо, но как выглядит этот Consumer
?
из каналов .generic.websocket import AsyncJsonWebsocketConsumer class NotificationConsumer (AsyncJsonWebsocketConsumer):
async def connect (self):
await self.accept ()
async def notify (self, event):
"This" вызовы в другом месте этой кодовой базы, которые выглядят
как:
channel_layer.group_send (имя_группы, {
'type': 'notify', # Это направляет его этому обработчику.
'content': json_message,
})
Не пытайтесь напрямую использовать send_json или что-то еще; эта развязка
поможет вам по мере роста.
"" "
await self.send_json (event [" content "])
async def receive_json (self, content, ** kwargs):
" ""
Обрабатывает данные, передаваемые по сети от клиента.
Нам необходимо проверить, что полученные данные имеют правильную форму
. Вы можете сделать это с помощью простого сериализатора DRF.
Затем нам нужно использовать эти проверенные данные, чтобы подтвердить, что запрашивающий
пользователь (доступен в self.scope ["user"] из-за
использования channels.auth.AuthMiddlewareStack в маршрутизации)
разрешено подписаться на запрошенный объект.
"" "
serializer = self.get_serializer (data = content)
if not serializer.is_valid ():
return
group_name = serializer.get_group_name ()
self.groups.append (group_name)
await self.channel_layer. group_add (
имя_группы,
self.channel_name,
)
def get_serializer (self, *, data):
А теперь вы захотите добавить кое-что в свой модуль маршрутизации
:
из django.urls import path from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from .consumers import NotificationConsumer
websockets = URLRouter ([
path (
"ws / notifications /",
path (
"ws / notifications /",
) "ws_notifications",
),
])
application = ProtocolTypeRouter ({
"websocket": AuthMiddlewareStack (websockets),
})
Есть еще пара штук.Нам действительно нужно отправлять обновления, когда
модельные изменения!
Мы разделяем эти опасения. Мы добавляем модуль уведомлений
с
соответствующие функции для упаковки данных и отправки их по каналам
слой, а затем мы вызываем эти функции в модели save
методы.
Во-первых, модуль уведомлений
: мы определяем асинхронную функцию, которая будет
построить и отправить объект соответствующей формы соответствующей группе
на канальном слое.Это часть нашего API и результат работы всех
здесь должны быть задокументированы вспомогательные функции для всех, кто использует этот API.
из channels.layers import get_channel_layer
from .serializers import FooSerializer async def update_foo (foo):
serializer = FooSerializer (foo)
group_name = serializer.get_group_name ()
channel_layer = get_channel_layer ()
content_layer (
content_layer () ":" UPDATE_FOO ",
" payload ": serializer.data,
}
await channel_layer.group_send (имя_группы, {
"type": "notify",
"content": content,
})
И затем наши модели
полагаются на три вещи: переопределение в сохранение
метод FieldTracker
из django-model-utils
и вызов
метод обновления из уведомлений
, завернутых в
asgiref.sync.async_to_sync
. Это выглядит так:
из django.db импортировать модели
из model_utils импортировать FieldTracker
из asgiref.sync import async_to_sync class Foo (models.Model):
tracker = FieldTracker (fields = ("bar",))
bar = models.CharField (max_length = 100)
def save (self, * args, ** kwargs ):
ret = super (). Save (* args, ** kwargs)
has_changed = self.tracker.has_changed ("bar")
if has_changed:
async_to_sync (update_foo) (self)
return ret
Тестирование
Тестирование асинхронного кода с помощью pytest лучше всего выполнять с помощью pytest-asyncio.
пакет. Это позволяет вам писать тесты, которые сами по себе являются асинхронными.
функций, если вы используете @pytest.маркер mark.asyncio
на них. В
В документации по каналам есть более подробная информация о том, как таким образом тестировать потребителей.
Одно предостережение, которое я могу предложить, — обязательно прочтите мнение своего потребителя на
каждая точка, где вы ожидаете получить новые данные, иначе ваши тесты могут упасть
вниз с трудно диагностируемыми ошибками тайм-аута. Так что ваши тесты будут выглядеть
немного вроде этого:
connected, _ = await communator.connect ()
assert connected await communator.send_json_to ({
"модель": "as.Соответствующий ",
" id ": str (some_model.id),
})
assert await communator.receive_nothing ()
await some_notification_async_function ()
response = await communator.receive_json_from ()
assert response == {
}
ожидание коммуникатора. Разъединение ()
Последние мысли
Это, конечно, работа. Когда мы сглаживаем петли, я
намереваются заключить легко изолированные фрагменты логики в пакет, который мы
могу раздавать.Я думаю, что это будет касаться конкретного потребителя
,
миксин сериализатора, миксин модели и конкретный модуль уведомлений.
Одна конкретная проблема, которую мы обнаружили, но еще не решила, — это то, что происходит
когда вы меняете сериализатор на основе запрашивающего пользователя. Например,
если вы хотите показать только ограниченную версию пользователя, если она не
пользователь запрашивает свою информацию, как мы справляемся с этим, когда
сериализация для веб-сокета? У меня пока нет хорошего ответа.
Сообщите нам, если вы попробуете это, или у вас есть идеи по улучшению! Это новый
почва для меня, и я бы хотел увидеть ее с разных точек зрения.
Недавно активных «django-каналов» вопросов
- Дом
- Лучшие теги
- JavaScript
- Java
- c #
- питон
- андроид
- php
- jquery
- c ++
- html
- ios
- css
- sql
- MySQL
- .нетто
- с
- г
- asp.net
- рубин на рельсах
- объектив-c
- массивов
- node.js
- sql-сервер
- iphone
- регулярное выражение
- рубин
- угловой
- json
- свифт
- Джанго
- Linux
- Топ-пользователи
- Алексей
- Крис
- Иоанна
- Дэвид
- Майк
- Майкл
- Сэм
- матовый
- Том
- Марка
- Бен
- Андрей
- Джеймс
- Ник
- Дан
- Даниил
- Пол
- Джейсон
- Дэйв
- Петр
- Тим
- Джо
- Стив
- Райан
- Адам
- Мартин
- Кевин
- макс
- домкрат
- Томас
- Дом
- вопросов
- Теги
- Пользователей
.