Де повинні жити обробники сигналів у проекті джанго?


143

Я щойно почав впроваджувати слухачі сигналів в проект джанго. Поки я розумію, що вони є, і як ними користуватися. Мені важко зрозуміти, куди мені їх поставити. У документації з сайту django написано:

Де повинен жити цей код?

Ви можете розмістити сигнал обробки та реєстраційний код куди завгодно. Однак вам потрібно переконатися, що модуль, який він вводить, рано імпортується, щоб обробка сигналів була зареєстрована перед тим, як будь-які сигнали потрібно надсилати. Це робить models.py вашого додатка гарним місцем для реєстрації обробників сигналів.

Хоча це гарна пропозиція, маючи не модельні класи чи методи в моїх моделях.py, просто протирає мене неправильно.

Тож, яка найкраща практика / правило для зберігання та реєстрації обробників сигналів?

Відповіді:


41

Я насправді люблю робити їх методами самої моделі. Це зберігає все в одному класі, а значить, вам не доведеться турбуватися про імпорт.


2
А де ви зазвичай підключаєте обробники сигналів?
DataGreed

1
@DataGreed: у нижній частині відповідних models.py.
Даніель Роузмен

102
Якщо ви слухаєте сигнали, випромінювані цією моделлю, то розміщення всіх слухачів теж робить цілу вправу безглуздою, чи не так? Точка сигналів полягає в роз’єднанні. Чи не повинні слухачі жити з кодом, який цікавить ці віддалені події? Питання в тому, як забезпечити завантаження слухачів перед випромінювачами.
Джон Мі

У моєму випадку я хочу слухати сигнал моделі, Fooяка є частиною fooapp. Але приймач сигналу є розширенням і живе в іншому додатку (наприклад otherapp).
гуетлі

2
На думку Джона Мі, це не набагато інакше, ніж просто переосмислити save () тощо.
Метт

246

Це було додано до документації, коли було випущено Django 1.7 :

Строго кажучи, обробка сигналів та код реєстрації можуть жити де завгодно, хоча рекомендується уникати кореневого модуля програми та модуля його моделей, щоб мінімізувати побічні ефекти імпорту коду.

На практиці обробники сигналів зазвичай визначаються в підмодулі сигналів програми, до якої вони відносяться. Приймачі сигналів підключаються за готовим () методом вашого класу конфігурації програми. Якщо ви використовуєте декоратор приймача (), просто імпортуйте підмодуль сигналів всередині готового ().

Змінено в Django 1.7: Оскільки Ready () не існував у попередніх версіях Django, реєстрація сигналів зазвичай відбувалася в модулі моделей.

Найкраща практика - визначити ваших обробників у handlers.py в підмодулі сигналів, наприклад, у файлі, який виглядає так:

yourapp / signal / handlers.py :

from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    pass

Найкраще місце для реєстрації обробника сигналів - це в AppConfig програми, яка визначає його, використовуючи метод Ready () . Це буде виглядати приблизно так:

yourapp / apps.py :

from django.apps import AppConfig

class TasksConfig(AppConfig):
    name = 'tasks'
    verbose_name = "Tasks"

    def ready(self):
        import yourproject.yourapp.signals.handlers #noqa

Переконайтеся, що ви завантажуєте свій AppConfig, вказавши його безпосередньо в INSTALLED_APPS свого settings.py або в __init__додатку. Для отримання додаткової інформації див. Готову () документацію .

Примітка. Якщо ви надаєте сигнали і для інших додатків для прослуховування, __init__введіть їх у модуль сигналів, наприклад, у файл, який виглядає так:

yourapp / сигнали / __ init__.py

import django.dispatch

task_generate_pre_save = django.dispatch.Signal(providing_args=["task"])

Потім інша програма може прослуховувати ваш сигнал, імпортуючи та реєструючи його, наприклад from yourapp.signals import task_generate_pre_save. Відокремлення сигналів від ваших обробників забезпечує чистоту.

Інструкція для Django 1.6:

Якщо ви все ще застрягли на Django 1.6 або новішої версії, тоді ви зробите те ж саме (визначте ваших обробників у yourapp / signal / handlers.py), але замість того, щоб використовувати AppConfig, ви завантажите обробники через __init__.py з ваш додаток, наприклад щось подібне:

yourapp / __ init__.py

import signals

Це не так приємно, як використання методу Ready (), оскільки це часто викликає кругові проблеми з імпортом.


3
оскільки документальний запис каже, що ви перекриваєте готові, ви можете зробити щось на кшталт super (ReportsConfig, self) .ready () у випадку, якщо django коли-небудь вирішить заповнити готовий () чимось (на 1.7.0 він наразі порожній)
w- -

3
Я вважаю, що ця відповідь є найкращою, оскільки вона є єдиною для вирішення побічних ефектів від імпорту. Я приїхав сюди в пошуках кращих практик, тому що я прибираю програму, яка порушена саме через такі побічні ефекти. На жаль, програма працює на django 1.6, а найкращі практики працюють лише на django 1.7. Тимчасове вирішення пускання __init__сигналів імпорту не допоможе мені, тому мені цікаво, чи є інше місце, з якого я можу імпортувати сигнали, поки ми не будемо готові до оновлення до пізнішої версії джанго.
kasperd

Чи не повинно бути from . import handlers(чи подібне) в yourapp/signals/__init__.py?
dhobbs

Чи не слід також імпортувати куди-небудь модуль handlers.py? Я намагаюся це зробити, і, схоже, це не визначає обробник сигналу.
Андрес

1
fwiw Мені не знадобився yourproject.останній рядок блоку коду класу TaskConfig. У мене це працює саме з цією структурою, тому врахуйте це питання :)
Грег Калека

40

Я тільки що натрапив на це, і оскільки мої сигнали не пов'язані з моделлю, я подумав, що додам своє рішення.

Я реєструю різні дані навколо входу / виходу та потрібні для підключення django.contrib.auth.signals.

Я помістив обробники сигналів у signals.pyфайл, а потім імпортував сигнали з __init__.pyфайлу модуля, так як я вважаю, що це викликається, як тільки програма запускається (тестування з printзаявою говорить про те, що він викликається ще до того, як файл налаштувань буде прочитаний.)

# /project/__init__.py
import signals

і в signals.py

# /project/signals.py
from django.contrib.auth.signals import user_logged_in

def on_logged_in(sender, user, request, **kwargs):
    print 'User logged in as: \'{0}\''.format(user)

user_logged_in.connect(on_logged_in)

Я досить новачок у Django (/ python), тому я відкритий для всіх, хто скаже мені, що це жахлива ідея!


3
Це здається логічним, але я б радив це зробити на рівні додатків.
Нілс

2
Обережно, ця логіка, швидше за все, призведе до запуску повторних сигналів. user_logged_in.connect(on_logged_in)найімовірніше, слід проходити в dispatch_uidаргументі. Більше на docs.djangoproject.com/en/dev/topics/signals / ... .
Скотт Коутс

Дякую за це - добре знати. Я реєструю всі входи за допомогою цього методу (записуючи IP / агент користувача), і до цього часу не було жодних дублікатів - хоча це не означає, що невелика зміна рядка не спричинить проблем!
Уго Роджер-Браун

13

Я нещодавно прочитав цю статтю про кращі практики, коли мова йде про викладення ваших проектів / додатків, і це дозволяє припустити, що всі ваші власні сигнали диспетчера повинні надходити у файл, який називається signals.py. Однак це не повністю вирішує вашу проблему, оскільки вам все-таки потрібно десь імпортувати їх, і чим раніше вони імпортуються, тим краще.

Пропозиція моделі хороша. Оскільки ви вже визначили все у вашому signals.pyфайлі, воно не повинно займати більше рядка у верхній частині файлу. Це подібно до того, як admin.pyвикладається файл (з визначенням класу вгорі та кодом для реєстрації всіх спеціальних класів адміністратора внизу), якщо ви визначаєте свої сигнали, то з'єднуйте їх у тому самому файлі.

Сподіваюся, що це допомагає! Зрештою, це зводиться до того, що ви віддаєте перевагу.


1
Я також хотів помістити свої обробники сигналів у signals.pyфайл, але не знав, як його слід викликати після цього. Імпортуючи його у свій models.pyфайл, я отримав дуже чисте рішення, не «забруднюючи» свій файл models.py. Дякую! :)
Данило Барген

10
там є крос-імпорт: signals.py намагається імпортувати модель з models.py
Іван Вірабян

8

models.py та signals.py в кожному додатку були рекомендованими місцями для підключення сигналів, однак, на мою думку, вони не найкраще рішення для збереження сигналів та обробників. Диспетчеризація повинна бути причиною сигналів та обробників, винайдених у джанго.

Я довго боровся, і нарешті ми придумали рішення.

створити модуль з'єднувача в папці додатків

тому у нас є:

app/
    __init__.py
    signals.py
    models.py
    connectors.py

у додатку / connectors.py ми визначили обробники сигналів та підключили їх. Наведено приклад:

from signals import example_signal
from models import ExampleModel
from django.db.models.signals import post_save, post_delete

def hanndler(sender, *args, **kwargs):
    pass

post_save.connect(hander, sender=ExampleModel)

то в models.py ми додаємо наступний рядок у кінці файлу:

from app import connector

Тут все зроблено.

Таким чином, ми можемо поставити сигнали в signals.py, а всі обробники в connectors.py. Ніякого безладу в моделях та сигналах.

Сподіваюсь, це забезпечує інше рішення.


1
Отже, що йде в signals.py? З вашого прикладу, схоже, це лише власні сигнали. Зазвичай ми просто поєднуємо сигнали та роз'єми, оскільки більшість не матиме власні сигнали.
dalore

@dalore так, всі власні сигнали передаються в signals.py. У нас багато настроєних сигналів. Але якщо вас не так багато, цей файл може бути пропущено.
Самуїл

те саме питання, що і @dal
olleh

1
зауважте, що все це тепер стара порада, спосіб django зараз - використовувати appconfig для імпорту обробників, де живуть обробники сигналів. А в signals.py йдуть користувацькі сигнали
сміливе

3

Я зберігаю їх в окремому файлі signals.py, In models.pyпісля того, як всі моделі визначені. Я імпортую їх і підключаю моделі до сигналів.

signals.py

#  necessary imports

def send_mail_on_save(<args>):
    # code here 

models.py

# imports
class mymodel(models.Model):
    # model here

# import signals
from signals import send_mail_on_save
# connect them 
post_save.connect(send_mail_on_save,sender=mymodel)

Це дає мені логічне розмежування, звичайно, немає нічого поганого в тому, щоб зберігати їх у models.py , але це більш керовано таким чином.

Сподіваюся, це допомагає !!


ви поміщаєте обробники сигналів у "signals.py", що, якщо ми називаємо це "handlers.py"
Абдул Фатах

1
Не має значення, чи називаєте ви файл signals.py чи handler.py. Це просто умова, а не правило.
відсиджували

3

Невелике нагадування про AppConfig. Не забудьте встановити:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.