سیگنال‌ها در Django: راهنمای کامل استفاده، اتصال، ارسال و مدیریت Signalها

این مقاله سیستم سیگنال‌های Django را توضیح می‌دهد؛ مکانیزمی برای اطلاع‌رسانی رویدادها بین بخش‌های مختلف برنامه بدون ایجاد وابستگی مستقیم. موضوعاتی مانند نحوهٔ اتصال receiverها، استفاده از decorator، مدیریت senderهای خاص، محل مناسب قرارگیری کد سیگنال‌ها، هشدارهای مهم، و بهترین شیوه‌ها بررسی می‌شوند.

Django Signals، سیگنال جنگوreceiver، senderrequest_finished، pre_save، اتصال سیگنال، decoupled apps

~5 دقیقه مطالعه · آخرین به‌روزرسانی ۲۴ اسفند ۱۴۰۴

سیگنال‌ها در Django چیست؟

Django یک «signal dispatcher» داخلی دارد که به بخش‌های مختلف برنامه اجازه می‌دهد بدون وابستگی مستقیم، از وقوع یک رویداد مطلع شوند. سیگنال‌ها زمانی مفید هستند که چندین بخش از کد نیاز داشته باشند از یک اتفاق مشترک باخبر شوند.

مثلاً یک اپلیکیشن ثالث می‌تواند از تغییر تنظیمات Django مطلع شود:


from django.apps import AppConfig
from django.core.signals import setting_changed

def my_callback(sender, **kwargs):
    print("Setting changed!")

class MyAppConfig(AppConfig):
    def ready(self):
        setting_changed.connect(my_callback)

هشدار: سیگنال‌ها ظاهر «loosely coupled» دارند، اما استفادهٔ زیاد از آن‌ها می‌تواند کد را پیچیده و سخت‌دیباگ کند. هرجا ممکن باشد، بهتر است کد را مستقیم فراخوانی کنید.


گوش دادن به سیگنال‌ها

برای دریافت یک سیگنال باید یک receiver ثبت کنید. این کار با Signal.connect() انجام می‌شود.

پارامترهای connect()

  • receiver: تابعی که هنگام ارسال سیگنال فراخوانی می‌شود
  • sender: فقط سیگنال‌هایی که از یک sender خاص ارسال شده‌اند را دریافت می‌کند
  • weak: اگر False نباشد، ممکن است تابع جمع‌آوری زباله شود
  • dispatch_uid: جلوگیری از ثبت تکراری سیگنال

تعریف یک Receiver


def my_callback(sender, **kwargs):
    print("Request finished!")

همهٔ receiverها باید sender و **kwargs را بپذیرند. حتی اگر سیگنال فعلاً آرگومانی ارسال نکند، ممکن است در آینده تغییر کند.

Receiverهای async


async def my_callback(sender, **kwargs):
    await asyncio.sleep(5)
    print("Request finished!")

Django به‌طور خودکار async یا sync بودن receiver را مدیریت می‌کند.


اتصال Receiver به سیگنال

روش ۱: اتصال دستی


from django.core.signals import request_finished

request_finished.connect(my_callback)

روش ۲: استفاده از decorator


from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

این روش خواناتر و استانداردتر است.


کد سیگنال‌ها باید کجا قرار بگیرد؟

بهترین محل برای قرار دادن سیگنال‌ها:

  • در یک فایل signals.py داخل اپلیکیشن
  • اتصال سیگنال‌ها در متد ready() کلاس AppConfig

مثال:


from django.apps import AppConfig

class MyAppConfig(AppConfig):
    def ready(self):
        from . import signals

این کار باعث می‌شود decoratorها به‌طور ضمنی سیگنال‌ها را ثبت کنند.


اتصال به سیگنال‌های یک Sender خاص

گاهی فقط می‌خواهید سیگنال‌هایی را دریافت کنید که از یک مدل خاص ارسال شده‌اند. مثلاً سیگنال pre_save قبل از ذخیرهٔ هر مدل ارسال می‌شود، اما شما فقط مدل MyModel را می‌خواهید:


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):
    ...

در این حالت فقط ذخیرهٔ MyModel باعث اجرای handler می‌شود.


نکات مهم دربارهٔ سیگنال‌ها

  • سیگنال‌ها می‌توانند کد را پیچیده کنند—با احتیاط استفاده کنید
  • receiverها باید همیشه **kwargs را بپذیرند
  • برای جلوگیری از ثبت تکراری، از dispatch_uid استفاده کنید
  • در تست‌ها ممکن است ready() چندبار اجرا شود

جمع‌بندی

سیگنال‌ها در Django ابزاری قدرتمند برای اطلاع‌رسانی رویدادها بین بخش‌های مختلف برنامه هستند. با تعریف receiverها، استفاده از decoratorها، مدیریت senderهای خاص و قرار دادن کد در محل مناسب، می‌توانید سیستم‌های انعطاف‌پذیر و قابل‌گسترش بسازید. با این حال، همیشه باید مراقب باشید که استفادهٔ بیش‌ازحد از سیگنال‌ها باعث پیچیدگی غیرضروری نشود.

جلوگیری از ثبت تکراری سیگنال‌ها

وقتی dispatch_uid مشخص نشده باشد، Django هویت receiver را بر اساس object identity تشخیص می‌دهد. برای توابع سطح ماژول، متدهای استاتیک و متدهای کلاس، این هویت ثابت است؛ بنابراین اتصال دوبارهٔ همان receiver هیچ تغییری ایجاد نمی‌کند:


def my_handler(sender, **kwargs):
    ...

my_signal.connect(my_handler)  # اجرای دوبارهٔ این خط بی‌اثر است.

اما متدهای وابسته به نمونه (Bound Methods) متفاوت‌اند

متدهای وابسته به نمونه (self) هویت متفاوتی برای هر instance دارند. بنابراین هر بار که یک instance جدید ساخته شود، متد آن یک receiver جدید محسوب می‌شود:


def connect_signals():
    backend = Backend()
    my_signal.connect(backend.my_handler)

connect_signals()  # هر بار یک receiver جدید ثبت می‌شود.

برای جلوگیری از این مشکل، از dispatch_uid استفاده کنید:


from django.core.signals import request_finished

request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

تعریف سیگنال‌های سفارشی

می‌توانید سیگنال‌های اختصاصی خود را با django.dispatch.Signal تعریف کنید:


import django.dispatch

pizza_done = django.dispatch.Signal()

نکتهٔ مهم: اگر sender و receiver هر دو داخل پروژهٔ شما هستند، معمولاً بهتر است به‌جای سیگنال از فراخوانی مستقیم تابع استفاده کنید.


ارسال سیگنال‌ها

Django چهار روش برای ارسال سیگنال دارد:

  • send() — هم‌زمان، بدون مدیریت خطا
  • send_robust() — هم‌زمان، با مدیریت خطا
  • asend() — غیرهم‌زمان (async)، نیازمند await
  • asend_robust() — غیرهم‌زمان، با مدیریت خطا

مثال ارسال سیگنال سفارشی


class PizzaStore:
    def send_pizza(self, toppings, size):
        pizza_done.send(
            sender=self.__class__,
            toppings=toppings,
            size=size
        )

تمام متدهای ارسال سیگنال یک لیست از زوج‌ها برمی‌گردانند:


[(receiver, response), ...]

تفاوت send و send_robust

  • send() — اگر receiver خطا بدهد، ارسال متوقف می‌شود
  • send_robust() — خطاها را می‌گیرد و به ارسال ادامه می‌دهد

خطاهای send_robust در ویژگی __traceback__ ذخیره می‌شوند.


ارسال سیگنال به‌صورت async

asend() و asend_robust() coroutine هستند و باید await شوند:


async def asend_pizza(self, toppings, size):
    await pizza_done.asend(
        sender=self.__class__,
        toppings=toppings,
        size=size
    )

نحوهٔ مدیریت sync و async توسط Django

  • receiverهای async با asyncio.gather() به‌صورت هم‌زمان اجرا می‌شوند
  • receiverهای sync هنگام asend با sync_to_async() فراخوانی می‌شوند
  • receiverهای async هنگام send با async_to_sync() فراخوانی می‌شوند

Django برای کاهش هزینهٔ تبدیل sync/async، ابتدا receiverها را گروه‌بندی می‌کند. به همین دلیل ممکن است ترتیب اجرای receiverها دقیقاً مطابق ترتیب ثبت‌شان نباشد.


قطع اتصال سیگنال‌ها

برای حذف یک receiver از سیگنال:


Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)

نتیجهٔ فراخوانی:

  • True — یک receiver حذف شد
  • False — receiver پیدا نشد
  • None — اگر sender یک reference تنبل باشد

اگر از dispatch_uid استفاده کرده باشید، نیازی به ارسال تابع receiver نیست.


جمع‌بندی

سیگنال‌ها در Django ابزار قدرتمندی برای ارتباط رویدادمحور بین بخش‌های مختلف برنامه هستند. با استفاده از dispatch_uid می‌توانید از ثبت تکراری جلوگیری کنید. با متدهای send()، send_robust()، asend() و asend_robust() می‌توانید سیگنال‌ها را در حالت‌های sync و async ارسال کنید. در نهایت، با disconnect() کنترل کامل حذف receiverها را در اختیار دارید.

نوشته و پژوهش‌شده توسط دکتر شاهین صیامی

مقالات مرتبط

فریم‌ورک Tasks در Django: راهنمای کامل اجرای کارهای پس‌زمینه در Django 6.0

Django 6.0 فریم‌ورک Tasks را معرفی کرده است؛ سیستمی داخلی برای تعریف و صف‌بندی کارهای پس‌زمینه خارج از چرخهٔ request–response. این مقاله نحوهٔ کار Tasks، پیکربندی backendها، تعریف و enqueue کردن Taskها، استفاده از context، و ادغام با سیستم‌های Worker خارجی را توضیح می‌دهد.

ادامه

پشتیبانی Asynchronous در Django: راهنمای کامل Async Views، ORM، Middleware، عملکرد و ایمنی

این مقاله پشتیبانی Django از برنامه‌نویسی غیرهم‌زمان (async) را توضیح می‌دهد. موضوعاتی مانند async views، اجرای Django تحت ASGI، رفتار middleware، قابلیت‌های ORM غیرهم‌زمان، sync_to_async، مدیریت قطع اتصال، نکات عملکردی، و مکانیزم‌های ایمنی Django برای جلوگیری از استفادهٔ ناامن async بررسی می‌شوند.

ادامه

System Check Framework در Django: راهنمای کامل نوشتن، ثبت، اجرا و تست چک‌های سیستمی

System Check Framework در Django مجموعه‌ای از بررسی‌های استاتیک برای تشخیص مشکلات رایج در پروژه است. این مقاله نحوهٔ اجرای چک‌ها، نوشتن چک‌های سفارشی، مدیریت پیام‌ها، برچسب‌گذاری چک‌ها، افزودن چک به مدل‌ها و فیلدها، و نوشتن تست‌های واحد و یکپارچه برای چک‌ها را توضیح می‌دهد.

ادامه

آشنایی کامل با Django Settings: پیکربندی، مدیریت محیط، و بهترین شیوه‌ها

این مقاله یک راهنمای جامع دربارهٔ سیستم تنظیمات در Django ارائه می‌دهد. موضوعاتی مانند ساختار فایل تنظیمات، نحوهٔ تعیین DJANGO_SETTINGS_MODULE، استفاده از settings در کد، امنیت، پیکربندی دستی با configure()، استفادهٔ مستقل از Django با django.setup() و ایجاد تنظیمات سفارشی بررسی می‌شوند.

ادامه

سریال‌سازی و دسریال‌سازی در Django: راهنمای کامل کار با Serialization Framework

این مقاله سیستم سریال‌سازی Django را توضیح می‌دهد؛ از نحوهٔ تبدیل مدل‌ها به JSON، XML، YAML و JSONL گرفته تا دسریال‌سازی داده‌ها، سریال‌سازی فیلدهای انتخابی، کار با مدل‌های ارث‌بری، مدیریت روابط، و نکات مهم هنگام ذخیرهٔ داده‌های دسریال‌شده.

ادامه

بهینه‌سازی و افزایش کارایی در Django: راهنمای کامل Performance، Benchmarking، Caching و Laziness

این مقاله تکنیک‌ها و ابزارهای مهم برای بهبود کارایی در Django را بررسی می‌کند. موضوعاتی مانند تعریف اهداف بهینه‌سازی، Benchmarking، ابزارهای تحلیل عملکرد، انتخاب سطح مناسب برای انجام عملیات، استفاده از Caching، درک Laziness و رفتار QuerySetها توضیح داده شده است.

ادامه