Sinyal işleyicileri bir django projesinde nerede yaşamalıdır?


143

Bir django projesinde sinyal dinleyicileri uygulamaya başladım. Ben ne olduklarını ve nasıl kullanılacağını anladım. Onları nereye koymam gerektiğini anlamakta zorlanıyorum. Django sitesindeki belgeler şunları söylemektedir:

Bu kod nerede yaşamalıdır?

Sinyal işleme ve kayıt kodunu istediğiniz yere koyabilirsiniz. Ancak, içinde bulunan modülün erken alındığından emin olmanız gerekir, böylece herhangi bir sinyal gönderilmeden önce sinyal işleme kaydedilir. Bu, uygulamanızın models.py'sini sinyal işleyicilerinin kaydını koymak için iyi bir yer yapar.

Modellerimde model olmayan sınıflara veya yöntemlere sahip olmak iyi bir öneri olsa da, sadece bana yanlış bir şekilde ovalar.

Peki, sinyal işleyicilerini saklamak ve kaydetmek için en iyi uygulama / kural nedir?

Yanıtlar:


41

Aslında onları modelin kendi sınıf yöntemlerini yapmaktan hoşlanıyorum. Bu, her şeyi tek bir sınıfta tutar ve hiçbir şey içe aktarma konusunda endişelenmenize gerek olmadığı anlamına gelir.


2
Ve genellikle sinyalleri işleyicilere nerede bağlarsınız?
DataGreed

1
@DataGreed: ilgili modellerin alt kısmında .py.
Daniel Roseman

102
Eğer bu modelin yaydığı sinyalleri dinliyorsanız, o zaman tüm dinleyicileri oraya koymak tüm egzersizi anlamsız kılar, değil mi? Sinyallerin noktası ayrıştırmaktır. Dinleyiciler bu uzak olaylarla ilgilenen kodla yaşamamalı mı? Soru, dinleyicilerin yayıcılardan önce yüklenmesini nasıl sağlayacağınızdır.
John Mee

Benim durumumda Foo, parçası olan bir model sinyali dinlemek istiyorum fooapp. Ancak sinyal alıcı bir uzantıdır ve farklı bir uygulamada (örneğin otherapp) yaşar .
guettli

2
John Mee'nin amacına göre, sadece save (), vs.'yi geçersiz kılmaktan çok farklı değil
Matt

246

Bu, Django 1.7 piyasaya çıktığında belgelere eklendi :

Kesin olarak, sinyal işleme ve kayıt kodu istediğiniz yerde yaşayabilir, ancak kodun içe aktarılmasının yan etkilerini en aza indirmek için uygulamanın kök modülünden ve model modülünden kaçınmanız önerilir.

Pratikte, sinyal işleyiciler genellikle ilişkili oldukları uygulamanın bir sinyal alt modülünde tanımlanır. Sinyal alıcıları, uygulama yapılandırma sınıfınızın ready () yöntemine bağlanır. Receiver () dekoratörünü kullanıyorsanız, ready () içindeki sinyaller alt modülünü içe aktarmanız yeterlidir.

Django 1.7'de değiştirildi: Django'nun önceki sürümlerinde ready () bulunmadığından, sinyal kaydı genellikle modeller modülünde gerçekleşti.

En iyi uygulama, işleyicilerinizi handlers.py dosyasında sinyaller alt modülünde, örneğin şuna benzer bir dosya olarak tanımlamaktır:

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

Sinyal işleyicinizi kaydetmek için en iyi yer, hazır () kullanarak uygulamayı tanımlayan uygulamanın AppConfig dosyasındadır. yöntemini . Bu şöyle görünecektir:

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

Doğrudan ayarlarınızda veya __init__uygulamanızda INSTALLED_APPS ayarında belirterek AppConfig'nizi yüklediğinizden emin olun . Daha fazla bilgi için hazır () belgelerine bakın.

Not: Diğer uygulamaların da dinlemesi için sinyal sağlıyorsanız, bunları __init__sinyal modülünüze, örneğin aşağıdaki gibi bir dosyaya yerleştirin:

YourApp / sinyaller / __ init__.py

import django.dispatch

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

Daha sonra başka bir uygulama sinyalinizi içe aktararak ve kaydederek dinleyebilir, örn from yourapp.signals import task_generate_pre_save. Sinyallerinizi işleyicilerinizden ayırmak işleri temiz tutar.

Django 1.6 için talimatlar:

Hala Django 1.6 veya daha düşük bir sürümde kalıyorsanız, aynı şeyi yaparsınız (işleyicilerinizi yourapp / signal / handlers.py'de tanımlayın), ancak AppConfig kullanmak yerine, işleyicileri __init__.py üzerinden yüklersiniz. uygulamanız, örneğin:

YourApp / __ init__.py

import signals

Bu, genellikle dairesel içe aktarma sorunlarına neden olduğu için ready () yöntemini kullanmak kadar hoş değildir.


3
belgesel hazır geçersiz kıldığınızı söylediği gibi, django'nun bir şeyle hazır () doldurmaya karar vermesi durumunda super (ReportsConfig, self) .ready () gibi bir şey yapmak isteyebilirsiniz (şu anda boş olan 1.7.0'dan itibaren)
w- -

3
Bence bu cevap en iyisidir çünkü ithalatın yan etkilerini ele alan tek cevap budur. Buraya en iyi uygulamaları aramaya geldim, çünkü tam olarak bu tür yan etkilerden dolayı bozulan bir uygulamayı temizliyorum. Ne yazık ki uygulama django 1.6 üzerinde çalışıyor ve en iyi uygulamalar sadece django 1.7 üzerinde çalışıyor. __init__İthalat sinyallerine izin vermenin geçici çözümü benim için işe yaramaz, bu yüzden daha sonraki bir django sürümüne yükseltmeye hazır olana kadar sinyalleri içe aktarabileceğim başka bir yer olup olmadığını merak ediyorum.
kasperd

İçinde from . import handlers(veya benzeri) olmamalı mı yourapp/signals/__init__.py?
dhobbs

Ayrıca handlers.py modülünü bir yere aktarmamalısınız? Bunu deniyorum ve sinyal için işleyiciyi tanımlıyor gibi görünmüyor.
Andrés

1
fwiw yourproject.TaskConfig sınıfı kod bloğunun son satırında ihtiyacım yoktu . Ben tam olarak bu yapı ile çalışma var, bu yüzden bu qa düşünün :)
Greg Kaleka

40

Sadece bununla karşılaştım ve sinyallerim modelle ilgili olmadığından çözümümü ekleyeceğimi düşündüm.

Ben giriş / çıkış etrafında çeşitli veri giriş ve içine kanca gerekiyordu django.contrib.auth.signals.

Sinyal işleyicilerini bir signals.pydosyaya koydum ve sonra __init__.pymodül dosyasından sinyalleri aldım , çünkü uygulama başlar başlamaz bu çağrıldığına inanıyorum (bir printifade ile test , ayarlar dosyası okunmadan önce çağrıldığını gösteriyor.)

# /project/__init__.py
import signals

ve sinyaller halinde.

# /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) için oldukça yeniyim, bu yüzden bana bunun korkunç bir fikir olduğunu söyleyen herkese açıkım!


3
Bu mantıklı geliyor ama bunu uygulama düzeyinde yapmanızı öneririm.
Nils

2
Dikkatli olun, bu mantık büyük olasılıkla yinelenen sinyallerin ateşlenmesine neden olacaktır. user_logged_in.connect(on_logged_in)büyük olasılıkla dispatch_uidargümanı geçiyor olmalıdır . Daha fazla bilgi için docs.djangoproject.com/en/dev/topics/signals/… .
Scott Coates

Bunun için teşekkürler - bilmek güzel. Bu yöntemi (IP / kullanıcı aracısını kaydetme) kullanarak tüm girişleri günlüğe kaydediyorum ve şu ana kadar herhangi bir kopyam olmadı - bu, hatta küçük bir değişiklik sorun yaratmayacağı anlamına gelmiyor!
Hugo Rodger-Brown

13

Yakın zamanda projelerinizi / uygulamalarınızı düzenleme konusunda en iyi uygulamalar hakkında bu makaleyi okudum ve tüm özel dağıtım sinyallerinizinsignals.py . Ancak, sorununuzu tam olarak çözmez, çünkü bunları bir yere aktarmanız gerekir ve ne kadar erken içe aktarılırsa o kadar iyidir.

Model önerisi iyi bir öneridir. signals.pyDosyanızdaki her şeyi zaten tanımladığınızdan, dosyanın üst kısmındaki bir satırdan fazla sürmemelidir. Bu,admin.py dosyanın düzenlenme (üstte sınıf tanımları ve altta tüm özel yönetici sınıflarını kaydetme kodu ile), eğer sinyallerinizi tanımlarsanız, aynı dosyaya bağlayın.

Umarım yardımcı olur! Nihayetinde tercih ettiğiniz şeye iniyor.


1
Ayrıca sinyal işleyicilerimi bir signals.pydosyaya koymak istedim , ancak daha sonra nasıl çağırılması gerektiğini bilmiyordum. Bunu dosyama alarak, models.pymodels.py dosyamı "kirletmeden" çok temiz bir çözüm buldum. Teşekkür ederim! :)
Danilo Bargen

10
orada bir çapraz ithalat var:
signal.py models.py'den

8

Her uygulamadaki models.py ve signal.py, sinyalleri bağlamak için önerilen yerler olmuştur, ancak, sinyalleri ve işleyicileri gönderilmek için bence en iyi çözüm değildir. Sevkıyat, django'da icat edilmiş olan işaret ve işleyicilerin nedeni olmalıdır.

Uzun zamandır mücadele ediyordum ve sonunda çözümü bulduk.

uygulama klasöründe bir bağlayıcı modülü oluşturma

Böylece sahibiz:

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

app / connectors.py'de sinyal işleyicilerini tanımladık ve bağladık. Bir örnek verilmiştir:

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)

sonra models.py'de dosyanın sonuna aşağıdaki satırı ekliyoruz:

from app import connector

Burada her şey yapıldı.

Bu şekilde, signal.py'ye ve tüm işleyicileri connectors.py'ye sinyaller koyabiliriz. Modellerde ve sinyallerde karışıklık yok.

Umarım başka bir çözüm sağlar.


1
Öyleyse signal.py'de neler oluyor? Örneğinizden sadece özel sinyaller gibi görünüyor. Genellikle sinyalleri ve konektörleri birleştiririz, çünkü çoğu özel sinyale sahip değildir.
dalore

@dalore evet, tüm özel sinyaller signal.py içine konur. Birçok özelleştirilmiş sinyalimiz var. Ancak çok fazla dosyanız yoksa, bu dosya atlanabilir.
samuel

@dal
olleh ile

1
Tüm bunlar şimdi eski tavsiye, django yolu şimdi sinyal işleyicilerinin yaşadığı işleyicileri almak için appconfig kullanmaktır. Ve
signal.py

3

Onları ayrı bir dosyada saklıyorum signals.py, models.pysonra tüm modeller tanımlandıktan sonra. Onları içe aktarırım ve modelleri sinyallere bağlarım.

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)

Bu bana mantıklı bir ayrım sağlıyor, elbette onları saklamakta yanlış bir şey yok models.py'de , Ama bu şekilde daha yönetilebilir.

Bu yardımcı olur umarım!!


sinyal işleyicilerini "signal.py" ye koyuyorsunuz, ya "handlers.py" olarak isimlendirirsek
Abdul Fatah

1
Dosyayı signal.py veya handler.py olarak adlandırmanızın bir önemi yoktur. Bu sadece bir kural değil kural.
allsyed

3

Hakkında küçük bir hatırlatma AppConfig. Ayarlamayı unutmayın:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.