Trường hợp nên xử lý tín hiệu sống trong một dự án django?


143

Tôi vừa mới bắt đầu thực hiện các tín hiệu nghe trong một dự án django. Trong khi tôi hiểu chúng là gì và làm thế nào để sử dụng chúng. Tôi đang có một thời gian khó khăn để tìm ra nơi tôi nên đặt chúng. Các tài liệu từ trang django có điều này để nói:

Mã này nên sống ở đâu?

Bạn có thể đặt xử lý tín hiệu và mã đăng ký bất cứ nơi nào bạn muốn. Tuy nhiên, bạn sẽ cần đảm bảo rằng mô-đun được nhập sớm để xử lý tín hiệu được đăng ký trước khi gửi bất kỳ tín hiệu nào. Điều này làm cho mô hình ứng dụng của bạn trở thành một nơi tốt để đăng ký trình xử lý tín hiệu.

Mặc dù đó là một gợi ý tốt, nhưng việc có các lớp hoặc phương thức không phải là mô hình trong mô hình của tôi chỉ khiến tôi hiểu sai.

Vì vậy, thực tế / quy tắc tốt nhất để lưu trữ và đăng ký xử lý tín hiệu là gì?

Câu trả lời:


41

Tôi thực sự muốn làm cho chúng phân loại của chính mô hình. Điều đó giữ mọi thứ trong một lớp và có nghĩa là bạn không phải lo lắng về việc nhập bất cứ thứ gì.


2
Và nơi nào bạn thường kết nối xử lý với các tín hiệu?
DataGreed

1
@DataGreed: ở cuối mô hình có liên quan.
Daniel Roseman

102
Nếu bạn đang nghe các tín hiệu phát ra từ mô hình đó thì việc đặt tất cả các trình nghe ở đó cũng làm cho toàn bộ bài tập trở nên vô nghĩa, phải không? Điểm của các tín hiệu là để tách rời. Người nghe có nên sống với mã quan tâm đến các sự kiện từ xa này không? Câu hỏi là làm thế nào để đảm bảo người nghe được tải trước khi phát.
John Mee

Trong trường hợp của tôi, tôi muốn nghe một tín hiệu của mô hình Foolà một phần của fooapp. Nhưng bộ thu tín hiệu là một phần mở rộng và không tồn tại trong một ứng dụng khác (ví dụ otherapp).
guettli

2
Theo quan điểm của John Mee, nó không khác nhiều so với việc ghi đè lưu (), v.v.
Matt

245

Điều này đã được thêm vào tài liệu khi Django 1.7 được phát hành:

Nói một cách chính xác, xử lý tín hiệu và mã đăng ký có thể sống ở bất cứ đâu bạn muốn, mặc dù vậy, nên tránh mô-đun gốc của ứng dụng và mô-đun mô hình của ứng dụng để giảm thiểu tác dụng phụ của việc nhập mã.

Trong thực tế, các bộ xử lý tín hiệu thường được xác định trong một mô hình con tín hiệu của ứng dụng mà chúng liên quan đến. Bộ thu tín hiệu được kết nối theo phương thức ready () của lớp cấu hình ứng dụng của bạn. Nếu bạn đang sử dụng trình trang trí máy thu (), chỉ cần nhập mô hình con tín hiệu bên trong sẵn sàng ().

Thay đổi trong Django 1.7: Vì sẵn sàng () không tồn tại trong các phiên bản trước của Django, đăng ký tín hiệu thường xảy ra trong mô-đun mô hình.

Thực hành tốt nhất là xác định trình xử lý của bạn trong handlers.py trong một mô hình con tín hiệu, ví dụ: một tệp trông giống như:

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

Nơi tốt nhất để đăng ký trình xử lý tín hiệu của bạn là trong AppConfig của ứng dụng xác định nó, sử dụng phương thức ready () . Điều này sẽ trông như thế này:

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

Đảm bảo bạn đang tải AppConfig của mình bằng cách chỉ định trực tiếp trong INSTALLED_APPS của cài đặt của bạn hoặc trong __init__ứng dụng của bạn. Xem xem tài liệu sẵn sàng () để biết thêm thông tin.

Lưu ý: Nếu bạn cũng đang cung cấp tín hiệu cho các ứng dụng khác nghe, hãy đặt chúng vào __init__mô-đun tín hiệu của bạn, ví dụ: một tệp trông giống như:

yourapp / signal / __ init__.py

import django.dispatch

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

Một ứng dụng khác sau đó có thể nghe tín hiệu của bạn bằng cách nhập và đăng ký nó, ví dụ from yourapp.signals import task_generate_pre_save. Tách các tín hiệu của bạn từ xử lý của bạn giữ cho mọi thứ sạch sẽ.

Hướng dẫn cho Django 1.6:

Nếu bạn vẫn bị kẹt trên Django 1.6 trở xuống, thì bạn cũng sẽ làm điều tương tự (xác định trình xử lý của bạn trong yourapp / signal / handlers.py) nhưng thay vì sử dụng AppConfig, bạn sẽ tải trình xử lý qua __init__.py của ứng dụng của bạn, ví dụ như một cái gì đó như:

yourapp / __ init__.py

import signals

Điều này không tốt bằng sử dụng phương thức ready () vì nó thường gây ra các vấn đề nhập vòng tròn.


3
như documentaiton nói bạn ghi đè sẵn sàng, bạn có thể muốn làm một cái gì đó giống như siêu (ReportsConfig, tự) .ready () trong trường hợp django bao giờ quyết định cư sẵn sàng () với một cái gì đó (như của 1.7.0 nó hiện đang trống)
w- -

3
Tôi nghĩ rằng câu trả lời này là tốt nhất vì nó là người duy nhất giải quyết các tác dụng phụ từ hàng nhập khẩu. Tôi đến đây để tìm kiếm các thực tiễn tốt nhất, bởi vì tôi đang dọn dẹp một ứng dụng, nó bị hỏng chính xác do loại tác dụng phụ này. Than ôi, ứng dụng đang chạy trên django 1.6 và các hoạt động tốt nhất chỉ hoạt động trên django 1.7. Cách giải quyết tạm thời cho phép __init__tín hiệu nhập sẽ không hoạt động với tôi, vì vậy tôi tự hỏi liệu có nơi nào khác tôi có thể nhập tín hiệu cho đến khi chúng tôi sẵn sàng nâng cấp lên phiên bản django sau này không.
kasperd

Không nên có from . import handlers(hoặc tương tự) trong yourapp/signals/__init__.py?
dhobbs

Bạn có nên nhập mô-đun handlers.py ở đâu đó không? Tôi đang thử điều này và dường như không xác định được trình xử lý tín hiệu.
Andrés

1
fwiw Tôi không cần yourproject.dòng cuối cùng của khối mã lớp TaskConfig. Tôi đã làm việc này với chính xác cấu trúc này, vì vậy hãy xem xét điều này :)
Greg Kaleka

40

Tôi chỉ mới bắt gặp điều này, và vì tín hiệu của tôi không liên quan đến mô hình, tôi nghĩ tôi sẽ thêm giải pháp của mình.

Tôi đang đăng nhập nhiều dữ liệu khác nhau xung quanh đăng nhập / đăng xuất và cần phải kết nối django.contrib.auth.signals.

Tôi đã đặt các trình xử lý tín hiệu vào một signals.pytệp và sau đó nhập tín hiệu từ __init__.pytệp mô-đun, vì tôi tin rằng nó được gọi ngay khi ứng dụng khởi động (thử nghiệm với một printtuyên bố cho thấy rằng nó được gọi ngay cả trước khi tệp cài đặt được đọc.)

# /project/__init__.py
import signals

và trong 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)

Tôi khá mới với Django (/ python) vì vậy hãy cởi mở với bất cứ ai nói với tôi rằng đây là một ý tưởng tồi tệ!


3
Điều này cảm thấy hợp lý nhưng tôi khuyên bạn nên làm điều đó ở cấp ứng dụng.
Nils

2
Cẩn thận, logic này rất có thể sẽ dẫn đến tín hiệu trùng lặp được kích hoạt. user_logged_in.connect(on_logged_in)rất có thể sẽ được thông qua trong các dispatch_uidđối số. Thông tin khác tại docs.djangoproject.com/en/dev/topics/signals/ .
Scott Coates

Cảm ơn vì điều đó - tốt để biết. Tôi đang ghi nhật ký tất cả thông tin đăng nhập bằng phương pháp này (ghi IP / tác nhân người dùng) và cho đến nay vẫn chưa có bất kỳ bản sao nào - mặc dù điều đó không có nghĩa là một thay đổi nhỏ trong dòng sẽ không gây ra sự cố!
Hugo Rodger-Brown

13

Tôi chỉ vừa mới đọc này bài viết về thực hành tốt nhất khi nói đến bố trí các dự án của bạn / các ứng dụng, và nó cho thấy rằng tất cả các tín hiệu điều phối tùy chỉnh của bạn nên đi trong một tập tin gọi signals.py. Tuy nhiên, điều đó không giải quyết được hoàn toàn vấn đề của bạn, vì bạn vẫn cần nhập những thứ này ở đâu đó và chúng càng được nhập sớm thì càng tốt.

Đề xuất mô hình là một trong những tốt. Vì bạn đã xác định mọi thứ trong signals.pytệp của mình , nên không nên lấy nhiều hơn một dòng ở đầu tệp. Điều này tương tự như cách đặt admin.pytệp (với các định nghĩa lớp ở trên cùng và mã để đăng ký tất cả các lớp quản trị tùy chỉnh ở dưới cùng), nếu bạn xác định tín hiệu của mình thì hãy kết nối chúng trong cùng một tệp.

Mong rằng sẽ giúp! Cuối cùng, nó đi xuống những gì bạn thích.


1
Tôi cũng muốn đặt các trình xử lý tín hiệu của mình vào một signals.pytệp, nhưng không biết nên gọi nó như thế nào sau đó. Bằng cách nhập nó vào models.pytệp của mình , tôi đã có được một giải pháp rất rõ ràng, không "làm ô nhiễm" tệp mô hình của tôi. Cảm ơn bạn! :)
Danilo Bargen

10
có một quá trình nhập chéo ở đó: signal.txt cố gắng nhập mô hình từ
model.py

8

model.py và signal.txt trong mỗi ứng dụng là những nơi được đề xuất để kết nối tín hiệu, tuy nhiên, theo tôi, chúng không phải là giải pháp tốt nhất để giữ tín hiệu và trình xử lý được gửi đi. Công văn phải là lý do tín hiệu và xử lý được phát minh trong django.

Tôi đã vật lộn trong thời gian dài, và cuối cùng chúng tôi đã tìm ra giải pháp.

tạo một mô-đun kết nối trong thư mục ứng dụng

vì vậy chúng tôi có:

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

trong ứng dụng / Trình kết nối, chúng tôi đã xác định trình xử lý tín hiệu và kết nối chúng. Một ví dụ được cung cấp:

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)

sau đó trong mô hình, chúng tôi thêm dòng sau vào cuối tệp:

from app import connector

Tất cả mọi thứ được thực hiện ở đây.

Theo cách này, chúng ta có thể đặt tín hiệu vào signal.py và tất cả các trình xử lý trong Trình kết nối. Không có sự lộn xộn trong các mô hình và tín hiệu.

Hy vọng nó cung cấp một giải pháp khác.


1
Vì vậy, những gì đi trong signal.py? Hình như từ ví dụ của bạn, đó chỉ là các tín hiệu tùy chỉnh. Thông thường chúng ta chỉ kết hợp các tín hiệu và đầu nối vì hầu hết sẽ không có tín hiệu tùy chỉnh.
dinois

@datio có, tất cả các tín hiệu tùy chỉnh được đặt trong signal.py. Chúng tôi có nhiều tín hiệu tùy chỉnh. Nhưng nếu bạn không có nhiều, tập tin này có thể bị bỏ qua.
samuel

câu hỏi tương tự như @dal
olleh

1
lưu ý tất cả những điều này bây giờ là lời khuyên cũ, cách django bây giờ là sử dụng appconfig để nhập trình xử lý nơi trình xử lý tín hiệu sống. Và trong signal.py đi các tín hiệu tùy chỉnh
dinois

3

Tôi giữ chúng trong một tệp riêng biệt signals.py, models.pysau khi tất cả các mô hình được xác định. Tôi nhập chúng và kết nối các mô hình với tín hiệu.

tín hiệu

#  necessary imports

def send_mail_on_save(<args>):
    # code here 

mô hình

# 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)

Điều này cung cấp cho tôi tách logic, tất nhiên không có gì là sai vào việc giữ chúng trong models.py , Nhưng nó là dễ quản lý hơn theo cách này.

Hi vọng điêu nay co ich!!


bạn đang đặt trình xử lý tín hiệu vào "signal.py", nếu chúng ta đặt tên nó là "handlers.py"
Abdul Fatah

1
Không quan trọng bạn đặt tên tệp là signal.txt hay handler.py. Nó chỉ là một quy ước không cai trị.
ám chỉ

3

Nhắc nhở nhỏ về AppConfig. Đừng quên thiết lập:

# yourapp/__init__.py

default_app_config = 'yourapp.apps.RockNRollConfig'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.