Di mana penangan sinyal harus tinggal dalam proyek Django?

143

Saya baru saja mulai menerapkan pendengar sinyal dalam proyek Django. Sementara saya mengerti apa itu dan bagaimana menggunakannya. Saya mengalami kesulitan mencari tahu di mana saya harus meletakkannya. Dokumentasi dari situs Django mengatakan ini:

Di mana kode ini harus hidup?

Anda dapat meletakkan kode penanganan sinyal dan registrasi di mana saja Anda suka. Namun, Anda harus memastikan bahwa modul yang ada di dalamnya akan diimpor sejak awal sehingga penanganan sinyal terdaftar sebelum sinyal apa pun perlu dikirim. Ini menjadikan models.py aplikasi Anda tempat yang bagus untuk meletakkan pendaftaran penangan sinyal.

Sementara itu saran yang bagus, memiliki kelas atau metode non model di models.py saya hanya menggosok saya dengan cara yang salah.

Jadi, apa praktik / aturan terbaik untuk menyimpan dan mendaftarkan penangan sinyal?

Jason Webb
sumber

Jawaban:

41

Saya benar-benar suka membuat mereka metode dari model itu sendiri. Itu membuat semuanya dalam satu kelas, dan berarti Anda tidak perlu khawatir tentang mengimpor apa pun.

Daniel Roseman
sumber
2
Dan di mana Anda biasanya menghubungkan penangan ke sinyal?
DataGreed
1
@ DataGreed: di bagian bawah models.py yang relevan.
Daniel Roseman
102
Jika Anda mendengarkan sinyal yang dipancarkan oleh model itu, maka menempatkan semua pendengar di sana juga membuat seluruh latihan menjadi sia-sia, bukan? Titik sinyal adalah decouple. Bukankah seharusnya pendengar hidup dengan kode yang tertarik pada acara jarak jauh ini? Pertanyaannya adalah bagaimana memastikan pendengar dimuat sebelum emitor.
John Mee
Dalam kasus saya, saya ingin mendengarkan sinyal model Fooyang merupakan bagian dari fooapp. Tetapi penerima sinyal adalah ekstensi dan tidak hidup di aplikasi yang berbeda (misalnya otherapp).
guettli
2
Untuk poin John Mee, itu tidak jauh berbeda dari hanya mengeset save (), dll.
Matt
246

Ini ditambahkan ke dokumentasi ketika Django 1.7 dirilis:

Sebenarnya, penanganan sinyal dan kode registrasi dapat hidup di mana saja Anda suka, meskipun disarankan untuk menghindari modul root aplikasi dan modul modelnya untuk meminimalkan efek samping dari kode impor.

Dalam praktiknya, penangan sinyal biasanya didefinisikan dalam suatu submodul sinyal dari aplikasi yang mereka hubungkan. Penerima sinyal terhubung dalam metode ready () dari kelas konfigurasi aplikasi Anda. Jika Anda menggunakan dekorator penerima (), cukup impor submodule sinyal di dalam ready ().

Diubah dalam Django 1.7: Karena ready () tidak ada di versi sebelumnya Django, pendaftaran sinyal biasanya terjadi dalam modul model.

Praktik terbaik adalah mendefinisikan handler Anda di handlers.py dalam submodule sinyal, misalnya file yang terlihat seperti:

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

Tempat terbaik untuk mendaftarkan pengendali sinyal Anda adalah di AppConfig aplikasi yang mendefinisikannya, menggunakan metode ready () . Ini akan terlihat seperti ini:

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

Pastikan Anda memuat AppConfig dengan menetapkannya secara langsung di pengaturan Anda. INSTALLED_APPS Anda, atau di dalam __init__aplikasi Anda. Lihat lihat dokumentasi ready () untuk informasi lebih lanjut.

Catatan: Jika Anda juga memberikan sinyal bagi aplikasi lain untuk didengarkan, letakkan di __init__dalam modul sinyal Anda, misalnya file yang terlihat seperti:

yourapp / signal / __ init__.py

import django.dispatch

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

Aplikasi lain kemudian dapat mendengarkan sinyal Anda dengan mengimpor dan mendaftarkannya, mis from yourapp.signals import task_generate_pre_save. Memisahkan sinyal Anda dari handler Anda menjaga semuanya tetap bersih.

Instruksi untuk Django 1.6:

Jika Anda masih terjebak pada Django 1.6 atau lebih rendah, maka Anda akan melakukan hal yang sama (tentukan handler Anda di yourapp / signal / handlers.py) tetapi alih-alih menggunakan AppConfig, Anda akan memuat handler melalui __init__.py dari aplikasi Anda, mis. sesuatu seperti:

yourapp / __ init__.py

import signals

Ini tidak sebagus menggunakan metode ready () karena sering menyebabkan masalah impor melingkar.

Aidan
sumber
3
sebagai documentaiton mengatakan Anda menimpa siap, Anda mungkin ingin melakukan sesuatu seperti super (ReportsConfig, self) .ready () dalam kasus Django pernah memutuskan untuk mengisi siap () dengan sesuatu (pada 1,7.0 saat ini kosong)
w- -
3
Saya pikir jawaban ini adalah yang terbaik karena ini adalah satu-satunya untuk mengatasi efek samping dari impor. Saya datang ke sini mencari praktik terbaik, karena saya sedang membersihkan aplikasi, yang rusak persis karena efek samping semacam ini. Sayangnya aplikasi ini berjalan pada Django 1.6, dan praktik terbaik hanya bekerja pada Django 1.7. Solusi sementara untuk membiarkan __init__sinyal impor tidak berfungsi untuk saya, jadi saya ingin tahu apakah ada tempat lain tempat saya dapat mengimpor sinyal hingga kami siap untuk meningkatkan ke versi django yang lebih baru.
kasperd
Bukankah seharusnya ada from . import handlers(atau serupa) di yourapp/signals/__init__.py?
dhobbs
Bukankah seharusnya Anda juga mengimpor modul handlers.py di suatu tempat? Saya mencoba ini dan sepertinya tidak mendefinisikan pengendali untuk sinyal.
Andrés
1
fwiw saya tidak perlu yourproject.di baris terakhir dari blok kode kelas TaskConfig. Saya mendapatkan ini berfungsi dengan struktur ini, jadi pertimbangkan qa ini :)
Greg Kaleka
40

Saya baru saja menemukan ini, dan karena sinyal saya tidak terkait dengan model saya pikir saya akan menambahkan solusi saya.

Saya mencatat berbagai data di sekitar log in / log out, dan perlu terhubung django.contrib.auth.signals.

Saya telah memasukkan penangan sinyal ke dalam signals.pyfile, dan kemudian mengimpor sinyal dari __init__.pyfile modul, karena saya percaya ini dipanggil segera setelah aplikasi dijalankan (pengujian dengan printpernyataan menunjukkan bahwa itu dipanggil bahkan sebelum file pengaturan dibaca.)

# /project/__init__.py
import signals

dan di signal.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)

Saya cukup baru untuk Django (/ python) jadi saya terbuka untuk siapa saja yang mengatakan kepada saya bahwa ini adalah ide yang mengerikan!

Hugo Rodger-Brown
sumber
3
Ini terasa logis tetapi saya sarankan melakukannya di tingkat aplikasi.
Nils
2
Hati-hati, logika ini kemungkinan besar akan menghasilkan sinyal duplikat beign dipecat. user_logged_in.connect(on_logged_in)kemungkinan besar akan lewat dalam dispatch_uidargumen. Lebih lanjut di docs.djangoproject.com/en/dev/topics/signals/… .
Scott Coates
Terima kasih untuk itu - senang tahu. Saya mencatat semua login menggunakan metode ini (merekam IP / agen pengguna), dan belum memiliki duplikat sejauh ini - meskipun itu tidak berarti perubahan kecil di telepon tidak akan menyebabkan masalah!
Hugo Rodger-Brown
13

Saya baru saja membaca artikel ini tentang praktik terbaik ketika datang untuk memetakan proyek / aplikasi Anda, dan itu menunjukkan bahwa semua sinyal operator kustom Anda harus masuk dalam file bernama signals.py. Namun, itu tidak sepenuhnya menyelesaikan masalah Anda, karena Anda masih perlu mengimpor ini di suatu tempat, dan semakin awal mereka diimpor semakin baik.

Saran model itu bagus. Karena Anda sudah menentukan semua yang ada di signals.pyfile Anda , seharusnya tidak perlu lebih dari satu baris di bagian atas file. Ini mirip dengan cara admin.pyfile diletakkan (dengan definisi kelas di bagian atas dan kode untuk mendaftarkan semua kelas admin kustom di bagian bawah), jika Anda mendefinisikan sinyal Anda kemudian hubungkan mereka di file yang sama.

Semoga itu bisa membantu! Pada akhirnya tergantung pada apa yang Anda inginkan.

hora
sumber
1
Saya juga ingin meletakkan penangan sinyal saya di signals.pyfile, tetapi tidak tahu bagaimana seharusnya dipanggil setelah itu. Dengan mengimpornya di models.pyfile saya , saya mendapat solusi yang sangat bersih, tanpa "mencemari" file models.py saya. Terima kasih! :)
Danilo Bargen
10
ada impor silang di sana: sinyal.py mencoba mengimpor model dari
models.py
8

models.py dan signal.py di setiap aplikasi telah menjadi tempat yang disarankan untuk menghubungkan sinyal, namun, mereka bukan solusi terbaik, menurut saya, untuk menjaga agar sinyal dan penangan tetap dikirim. Pengiriman harus menjadi alasan sinyal dan penangan ditemukan di Django.

Saya berjuang untuk waktu yang lama, dan akhirnya kami menemukan solusinya.

buat modul konektor di folder aplikasi

jadi kita punya:

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

di app / connectors.py, kami mendefinisikan penangan sinyal dan menghubungkannya. Contoh disediakan:

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)

lalu di models.py, kita tambahkan baris berikut di akhir file:

from app import connector

Semuanya dilakukan di sini.

Dengan cara ini, kita bisa menaruh sinyal di dalam signal.py, dan semua penangan di connectors.py Tidak ada kekacauan dalam model dan sinyal.

Semoga ini memberikan solusi lain.

samuel
sumber
1
Jadi, apa yang ada di signal.py? Sepertinya dari contoh Anda itu hanya sinyal khusus. Biasanya kami hanya menggabungkan sinyal dan konektor karena sebagian besar tidak akan memiliki sinyal khusus.
dalore
@duruara ya, semua sinyal khusus dimasukkan ke dalam signal.py. Kami memiliki banyak sinyal khusus. Tetapi jika Anda tidak punya banyak, file ini bisa dihilangkan.
samuel
pertanyaan yang sama dengan @dal
olleh
1
perhatikan semua ini sekarang merupakan saran lama, cara Django sekarang adalah menggunakan appconfig untuk mengimpor handler di mana handler sinyal tinggal. Dan di signal.py pergi sinyal khusus
dalore
3

Saya menyimpannya dalam file terpisah signals.py, dalam models.pysetelah semua model didefinisikan. Saya mengimpornya dan menghubungkan model ke sinyal.

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

Ini memberikan saya pemisahan yang logis, tentu saja tidak ada salahnya menyimpannya dalam models.py , Tetapi lebih mudah dikelola dengan cara ini.

Semoga ini membantu!!

sekutu
sumber
Anda meletakkan penangan sinyal di "signal.py", bagaimana jika kita menamakannya sebagai "handlers.py"
Abdul Fatah
1
Tidak masalah apakah Anda memberi nama file sebagai signal.py atau handler.py. Itu hanya konvensi bukan aturan.
allsyed
3

Pengingat kecil tentang AppConfig. Jangan lupa untuk mengatur:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
valex
sumber