ผู้จัดการสัญญาณควรอยู่ที่ไหนในโครงการ django


143

ฉันเพิ่งเริ่มใช้งานฟังสัญญาณในโครงการ django ในขณะที่ฉันเข้าใจสิ่งที่พวกเขาและวิธีการใช้พวกเขา ฉันมีเวลายากลำบากในการหาที่ที่ฉันควรจะวางพวกเขา เอกสารจากเว็บไซต์ django มีสิ่งนี้ที่จะกล่าวว่า:

รหัสนี้ควรอยู่ที่ไหน

คุณสามารถใส่สัญญาณการจัดการและรหัสลงทะเบียนได้ทุกที่ที่คุณต้องการ อย่างไรก็ตามคุณจะต้องตรวจสอบให้แน่ใจว่าโมดูลนั้นได้รับการนำเข้าก่อนเพื่อให้การจัดการสัญญาณได้รับการลงทะเบียนก่อนที่จะต้องส่งสัญญาณใด ๆ สิ่งนี้ทำให้ model.py แอปของคุณเป็นที่ที่เหมาะสำหรับการลงทะเบียนตัวจัดการสัญญาณ

ในขณะที่มันเป็นคำแนะนำที่ดีการมีคลาสหรือวิธีการที่ไม่ใช่ model ใน models.py ของฉันเพียงแค่ถูฉันผิด

ดังนั้นวิธีปฏิบัติที่ดีที่สุดสำหรับการจัดเก็บและการลงทะเบียนตัวจัดการสัญญาณคืออะไร?

คำตอบ:


41

ฉันชอบที่จะทำให้พวกเขาเป็นคลาสวิธีของตัวเอง นั่นทำให้ทุกอย่างอยู่ในชั้นเดียวและหมายความว่าคุณไม่ต้องกังวลกับการนำเข้าอะไรเลย


2
และคุณมักจะเชื่อมต่อเครื่องจัดการกับสัญญาณที่ไหน?
DataGreed

1
@DataGreed: ที่ด้านล่างของ models.py ที่เกี่ยวข้อง
Daniel Roseman

102
หากคุณกำลังฟังสัญญาณที่ปล่อยออกมาจากรุ่นนั้นให้ผู้ฟังทั้งหมดที่นั่นทำให้การออกกำลังกายทั้งหมดไม่มีจุดหมายเลยเหรอ? จุดของสัญญาณคือการแยก ผู้ฟังไม่ควรอยู่กับโค้ดที่สนใจในเหตุการณ์ระยะไกลเหล่านี้หรือ คำถามคือทำอย่างไรเพื่อให้แน่ใจว่าผู้ฟังได้รับการโหลดก่อนที่อิมิเตอร์
John Mee

ในกรณีของฉันฉันต้องการที่จะฟังสัญญาณของรูปแบบซึ่งเป็นส่วนหนึ่งของFoo fooappแต่ตัวรับสัญญาณเป็นส่วนเสริมและใช้งานในแอปอื่น (ตัวอย่างotherapp)
guettli

2
สำหรับประเด็นของจอห์นมีก็ไม่ได้แตกต่างไปจากการเอาชนะ save () เป็นต้น
แมตต์

246

สิ่งนี้ถูกเพิ่มไปยังเอกสารเมื่อDjango 1.7เปิดตัว:

การพูดอย่างเคร่งครัดการจัดการสัญญาณและรหัสการลงทะเบียนสามารถอยู่ได้ทุกที่ที่คุณต้องการแม้ว่าจะแนะนำให้หลีกเลี่ยงโมดูลรูทของแอปพลิเคชันและโมดูลโมเดลเพื่อลดผลข้างเคียงของการนำเข้ารหัส

ในทางปฏิบัติตัวจัดการสัญญาณมักถูกกำหนดไว้ในสัญญาณ submodule ของแอปพลิเคชันที่เกี่ยวข้อง ตัวรับสัญญาณเชื่อมต่ออยู่ในวิธีการพร้อม () ของคลาสการกำหนดค่าแอปพลิเคชันของคุณ หากคุณกำลังใช้ตัวรับสัญญาณ () มัณฑนากรเพียงนำเข้าสัญญาณ submodule ภายในพร้อม ()

เปลี่ยนเป็น Django 1.7: เนื่องจาก ready () ไม่มีอยู่ใน Django เวอร์ชันก่อนหน้าการลงทะเบียนสัญญาณมักจะเกิดขึ้นในโมดูลโมเดล

วิธีปฏิบัติที่ดีที่สุดคือการกำหนดตัวจัดการของคุณในตัวจัดการ.pyในการส่งสัญญาณสัญญาณเช่นไฟล์ที่มีลักษณะดังนี้:

yourapp / สัญญาณ / ตัวจัดการไฟล์ :

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 ของ app ที่กำหนดมันใช้พร้อม ()วิธีการ จะมีลักษณะเช่นนี้:

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 ของคุณด้วยการระบุไว้โดยตรงใน settings.py ของ INSTALLED_APPS ของคุณโดยตรงหรือใน__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 / tires.py) แต่แทนที่จะใช้ AppConfig คุณจะโหลดตัวจัดการผ่านทาง __init__.py ของ แอปของคุณเช่นบางสิ่งเช่น:

yourapp / __ init__.py

import signals

วิธีนี้ไม่ดีเท่าการใช้วิธี ready () เพราะมักทำให้เกิดปัญหาการนำเข้าแบบวงกลม


3
ในขณะที่ documentaiton บอกว่าคุณแทนที่พร้อมคุณอาจต้องการทำอะไรบางอย่างเช่น super (ReportsConfig, self). ready () ในกรณีที่ django เคยตัดสินใจที่จะเติมพร้อม () กับบางสิ่ง (ณ 1.7.0 ขณะนี้ว่างเปล่า)
w- -

3
ฉันคิดว่าคำตอบนี้ดีที่สุดเพราะเป็นคำตอบเดียวที่จะแก้ไขผลข้างเคียงจากการนำเข้า ฉันมาที่นี่เพื่อค้นหาแนวทางปฏิบัติที่ดีที่สุดเพราะฉันกำลังล้างแอปพลิเคชันซึ่งเสียอย่างแน่นอนเนื่องจากผลข้างเคียงประเภทนี้ อนึ่งแอพพลิเคชั่นนั้นทำงานบน django 1.6 และแนวปฏิบัติที่ดีที่สุดนั้นใช้ได้กับ django 1.7 เท่านั้น วิธีแก้ปัญหาชั่วคราวของการให้__init__สัญญาณการนำเข้าไม่ทำงานสำหรับฉันดังนั้นฉันสงสัยว่ามีอีกที่หนึ่งที่ฉันสามารถนำเข้าสัญญาณจากจนกว่าเราจะพร้อมอัปเกรดเป็นรุ่น django ในภายหลัง
kasperd

ไม่ควรมีfrom . import handlers(หรือคล้ายกัน) ในyourapp/signals/__init__.py?
dhobbs

คุณไม่ควรนำเข้าตัวจัดการโมดูล ฉันพยายามทำสิ่งนี้และดูเหมือนจะไม่ได้กำหนดตัวจัดการสัญญาณ
Andrés

1
fwiw ฉันไม่ต้องการyourproject.ในบรรทัดสุดท้ายของบล็อคโค้ดคลาส TaskConfig ฉันมีการทำงานนี้กับว่าโครงสร้างนี้ดังนั้นจึงควรพิจารณา QA นี้ :)
เกร็ก Kaleka

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/th/dev/topics/signals/… .
Scott Coates

ขอบคุณสำหรับสิ่งนั้น - ดีน่ารู้ ฉันเข้าสู่ระบบการเข้าสู่ระบบทั้งหมดโดยใช้วิธีนี้ (บันทึก IP / ตัวแทนผู้ใช้) และยังไม่ได้ซ้ำกัน - แม้ว่านั่นไม่ได้หมายความว่าการเปลี่ยนแปลงเล็กน้อยบรรทัดจะไม่ทำให้เกิดปัญหา!
Hugo Rodger-Brown

13

ฉันเพิ่งอ่านนี้บทความเกี่ยวกับวิธีปฏิบัติที่ดีที่สุดเมื่อมันมาถึงการออกวางโครงการของคุณ / signals.pyโปรแกรมและมันแสดงให้เห็นว่าทุกสัญญาณมอบหมายงานของคุณเองควรจะไปในไฟล์ที่เรียกว่า อย่างไรก็ตามนั่นยังไม่สามารถแก้ปัญหาของคุณได้อย่างสมบูรณ์เนื่องจากคุณยังต้องนำเข้าสิ่งเหล่านี้ที่ใดที่หนึ่งและก่อนหน้านี้พวกเขาจะนำเข้าสิ่งที่ดีกว่า

คำแนะนำแบบจำลองเป็นสิ่งที่ดี เนื่องจากคุณได้กำหนดทุกอย่างในsignals.pyไฟล์ของคุณแล้วมันไม่ควรใช้มากกว่าบรรทัดที่ด้านบนของไฟล์ สิ่งนี้คล้ายกับวิธีการadmin.pyจัดวางไฟล์ (พร้อมคำจำกัดความของคลาสที่ด้านบนและรหัสสำหรับการลงทะเบียนคลาสผู้ดูแลระบบที่กำหนดเองทั้งหมดที่ด้านล่าง) หากคุณกำหนดสัญญาณของคุณจากนั้นเชื่อมต่อพวกเขาในไฟล์เดียวกัน

หวังว่าจะช่วย! ในที่สุดมันก็ลงมาในสิ่งที่คุณต้องการ


1
ฉันยังต้องการที่จะวางตัวจัดการสัญญาณของฉันไว้ในsignals.pyไฟล์ แต่ไม่รู้ว่ามันควรจะถูกเรียกหลังจากนั้นอย่างไร ด้วยการนำเข้ามาในmodels.pyไฟล์ของฉันทำให้ฉันมีวิธีแก้ไขที่สะอาดมากโดยไม่มี "มลพิษ" ไฟล์ models.py ของฉัน ขอบคุณ! :)
Danilo Bargen

10
มีการนำเข้าแบบไขว้ที่นั่น: signals.py พยายามนำเข้าโมเดลจาก models.py
Ivan Virabyan

8

models.py และ signals.py ในแต่ละแอพเป็นสถานที่ที่แนะนำในการเชื่อมต่อสัญญาณอย่างไรก็ตามพวกเขาไม่ใช่ทางออกที่ดีที่สุดในความคิดของฉันเพื่อให้ส่งสัญญาณและเครื่องมือจัดการ การส่งสัญญาณควรเป็นสัญญาณเหตุผลและเครื่องมือจัดการคิดค้นใน django

ฉันต้องดิ้นรนเป็นเวลานานและในที่สุดเราก็พบวิธีแก้ปัญหา

สร้างโมดูลตัวเชื่อมต่อในโฟลเดอร์แอพ

ดังนั้นเราจึงมี:

app/
    __init__.py
    signals.py
    models.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
แล้วจะมีอะไรใน signal.py? ดูเหมือนว่าจากตัวอย่างของคุณมันเป็นเพียงสัญญาณที่กำหนดเอง โดยปกติแล้วเราเพียงแค่รวมสัญญาณและตัวเชื่อมต่อเนื่องจากส่วนใหญ่จะไม่มีสัญญาณที่กำหนดเอง
dalore

@ Dalore ใช่สัญญาณที่กำหนดเองทั้งหมดจะถูกใส่ไว้ใน signals.py เรามีสัญญาณที่กำหนดเองมากมาย แต่ถ้าคุณมีไม่มากไฟล์นี้อาจถูกตัดออกไป
samuel

คำถามเดียวกันกับ @dal
olleh

1
โปรดทราบว่าทั้งหมดนี้เป็นคำแนะนำแบบเก่าวิธี django ในขณะนี้คือใช้ appconfig เพื่อนำเข้าตัวจัดการที่ตัวจัดการสัญญาณอยู่ และใน signals.py ไปสัญญาณที่กำหนดเอง
dalore

3

ผมเก็บไว้ในแฟ้มแยกต่างหากsignals.pyใน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" จะเกิดอะไรขึ้นถ้าเราตั้งชื่อมันเป็น "ตัวจัดการ"
Abdul Fatah

1
ไม่สำคัญว่าคุณจะตั้งชื่อไฟล์ว่า signals.py หรือ handler.py มันเป็นเพียงการประชุมที่ไม่ได้ปกครอง
allsyed

3

เตือนAppConfigเล็กน้อยเกี่ยวกับ อย่าลืมตั้งค่า:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.