Что такое сигнал в Django Framework ?
На бытовом уровне это система (диспетчер сигналов) которая обрабатывает некоторые виды событий которые генерирует система. По сути система сигналов разделяется на два компонента:
- sender — компонент посылающий сигнал;
- receiver — компонент отвечающий за обработку сигнала.
Опять же, пример на бытовом уровне.
Допустим у нас есть три модели:
class UserProfile(models.Model): nick = models.CharField(max_length = 30) score = models.FloatField(default = 0) class Post(models.Model): user_profile = models.ForeignKey(UserProfile) text = models.TextFiled() class UserAvatar(models.Model): user_profile = models.ForeignKey(UserProfile) image = models.UrlField()
И допустим у нас есть следующее условие: при каждом добавлении записи Post или UserAvatar должно обновляться поле UserProfile.score увеличиваясь на единицу.
Самый простой и первый приходящий в голову способ — это перегрузить методы save моделей Post и UserAvatar. Но пионэры не ищут простых путей, так что я заюзаю систему сигналов джанги, да и к тому-же зачем засорять лишней логикой save да еще и в двух моделях.
Вообще, сигналы, условно можно разделить на два типа:
- встроенные
- пользовательские
Встроенные разделяются на несколько подгрупп:
- Сигналы моделей. Данная группа самая обширная и, наверное, самая часто используемая. Она состоит из следующих сигналов:
- pre_init, post_init — посылаются соответственно до и после вызова метода __init__ модели;
- pre_save, post_save — соответственно посылаются до и после вызова метода save модели;
- pre_delete, post_delete — по аналогии, до и после удаления объекта (вызов метода delete);
- m2m_changed — отправляется при изменении объектов m2m (ManyToMany) связи;
- class_prepared — отправляется после регистрации модели в Djange. Как главсит документация it’s not generally used in third-party applications.
- Менеджментные сигналы (я не знаю как из нормально перевести на русский язык)
- pre_syncdb, post_syncdb — вызваются до и после запуска команды manage.py syncdb
- Request/response сигналы (аналогично, фантазия на перевод не работает)
- request_started — посылается когда Django начинает обработку request;
- request_finished — посылается после завершения обработки request;
- Тестовые сигналы — запускаются только когда запущены юнит тесты, группа состоит из 2-х сигналов setting_changed, template_rendered
- Сигналы генерируемые движком работы с БД (не путать с ORM). Содержит только сигнал connection_created который запускается после создания соединения с БД.
Для нашего примера, нам вполне хватит одного сигнала post_save. Почему именно его ? Потому что нам важно сделать инкрементацию поля score после сохранения моделей UserAvatar или Post, данный сигнал как раз за это и отвечает.
Сам сигнал «испускается» в кишках ORM Django (точнее за это отвечает Signal Dispatcher), наша задача только реализовать приемник (ни или обработчик сигнала) данного типа. Для этого в Django есть декоратор reciever(signal, **kwargs).
Вот код нашего обработчика:
from django.db.models.signals import post_save from django.dispatch import receiver @receiver(post_save, sender = Post) @receiver(post_save, sender = UserAvatar) def add_score(instance, **kwargs): profile = instance.user_profile profile.score += 1 profile.save()
Как видно, я использовал два декоратора для сигнала post_save от разных моделей. В метод add_save, который после «оборачивания» декоратором receiver стал приемником событий post_save, передается собственно объект который вызвал это событие. В нашем случае это могут быть объекты класса UserAvatar и Post. Так как обе модели имеют поле user_profile, то инкрментация поля score не представляет сложности (иначе бы пришлось проверять тип пришедшего объекта и делать условия, но имхо проще сделать 2 разных сигнала).
Кроме встроенных сигналов есть возможность создавать свои сигналы. Но это тема следующей заметки.
С сигналами связано два неприятных момента с которыми можно столкнуться при излишне активном использовании:
- Рекурсивный вызов сигналов. Когда один сигнал может запустить другой который в свою очередь вызовет первый. Тут важно внимательно следить за вызовами;
- «Размазывание» логики. Из за того что встроенные сигналы явным образом нигде не вызываются, то очень легко забыть, что например, сохранение какого либо объекта запускает череду сигналов. Из за этого бывают непредвиденные и сложно отлаживаемые баги (все вроде работает, ничего не падает а результат не тот что ожидается)
И последнее. Сигналы удобно отслеживать через Django Debug Toolbar. Для этого в настройках DEBUG_TOOLBAR_PANELS нужно добавить ‘debug_toolbar.panels.signals.SignalDebugPanel’
Спасибо за статью,но исправьте опечатки,очень уж их много
Здравствуте.
Спасибо за комментарий, постараюсь исправиться.
так и не исправился :) а статья хорошая
Получается если использовать этот пример, то score будет изменяться не только при добавлении, а и при изменении записей в БД?
Спасибо за статью, сигналы весьма полезная штука.
Менеджментные сигналы -> Управляющие сигналы
Как главсит документация -> Как гласит документация
вызваются до и после запуска -> вызываются до и после запуска
reciever(signal, **kwargs) -> receiver(signal, **kwargs)
Спасибо, стало многое понятно и очевидно!
» В метод
add_save,
который после «оборачивания» декоратором receiver стал приемником событий post_save, передается собственно объект который вызвал это событие. »
Имелось ввиду метод
add_score
наверное?