Nơi thích hợp để giữ tệp signal.py của tôi trong dự án Django


88

Dựa trên tài liệu của Django mà tôi đã đọc, có vẻ như signals.pytrong thư mục ứng dụng là một nơi tốt để bắt đầu, nhưng vấn đề tôi đang gặp phải là khi tôi tạo tín hiệu pre_savevà tôi cố gắng nhập lớp từ mô hình, nó xung đột với importtrong mô hình của tôi.

# models.py

from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext as _
from signals import *

class Comm_Queue(CommunicatorAbstract):
    queue_statuses = (
        ('P', _('Pending')),
        ('S', _('Sent')),
        ('E', _('Error')),
        ('R', _('Rejected')),
    )
    status          = models.CharField(max_length=10, db_index=True, default='P')
    is_html         = models.BooleanField(default=False)
    language        = models.CharField(max_length=6, choices=settings.LANGUAGES)
    sender_email    = models.EmailField()
    recipient_email = models.EmailField()
    subject         = models.CharField(max_length=100)
    content         = models.TextField()

# signals.py

from django.conf import settings
from django.db.models.signals import pre_save
from django.dispatch import receiver
from models import Comm_Queue

@receiver(pre_save, sender=Comm_Queue)
def get_sender_email_from_settings(sender, **kwargs):
    obj=kwargs['instance']
    if not obj.sender_email:
        obj.sender_email='%s' % settings.ADMINS[0][1]

Mã này sẽ không chạy vì tôi nhập Comm_Queuebên trong signals.pyvà tôi cũng nhập các tín hiệu bên trong models.py.

Bất cứ ai có thể cho lời khuyên về cách tôi có thể vượt qua vấn đề này?

Trân trọng


Câu trả lời:


65

Câu trả lời ban đầu, cho Django <1,7:

Bạn có thể đăng ký các tín hiệu bằng cách nhập signals.pyvào __init__.pytệp của ứng dụng :

# __init__.py
import signals

Điều này sẽ cho phép nhập models.pytừ signals.pymà không có lỗi nhập vòng tròn.

Một vấn đề với cách tiếp cận này là nó sẽ làm xáo trộn các kết quả về mức độ phù hợp nếu bạn đang sử dụng cover.py.

Thảo luận liên quan

Chỉnh sửa: Đối với Django> = 1.7:

Kể từ khi AppConfig được giới thiệu, cách nhập tín hiệu được khuyến nghị nằm trong init()chức năng của nó . Xem câu trả lời của Eric Marcos để biết thêm chi tiết.


6
sử dụng các tín hiệu trong Django 1.9, sử dụng phương pháp dưới đây (do django gợi ý). Phương pháp này không làm việc choAppRegistryNotReady("Apps aren't loaded yet.")
s0nskar

1
Eric Marcos câu trả lời của anh ấy nên là chấp nhận câu trả lời: stackoverflow.com/a/21612050/3202958 từ Django> = 1,7, sử dụng cấu hình ứng dụng
Nrzonline

1
Đã đồng ý. Tôi sẽ sửa câu trả lời cho điểm câu trả lời Eric Marcos cho Django 1.7+
yprez

194

Nếu bạn đang sử dụng Django <= 1.6, tôi khuyên bạn nên sử dụng giải pháp Kamagatos: chỉ cần nhập tín hiệu của bạn ở cuối mô-đun mô hình của bạn.

Đối với các phiên bản tương lai của Django (> = 1.7), cách được khuyến nghị là nhập mô-đun tín hiệu của bạn vào hàm ready () cấu hình của ứng dụng :

my_app/apps.py

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

my_app/__init__.py

default_app_config = 'my_app.apps.MyAppConfig'

7
Họ cũng đề cập trong tài liệu 1.7 rằng đôi khi sẵn sàng có thể được gọi nhiều lần và do đó, để tránh các tín hiệu trùng lặp, hãy đính kèm một số nhận dạng duy nhất vào lệnh gọi trình kết nối tín hiệu của bạn: request_ished.connect (my_callback, Dispatch_uid = "my_unique_identifier") Trong đó disp_uid thường là một chuỗi nhưng có thể là bất kỳ đối tượng có thể băm nào. docs.djangoproject.com/en/1.7/topics/signals/…
Emeka

13
Đây phải là câu trả lời được chấp nhận! Câu trả lời được chấp nhận ở trên ném một lỗi khi triển khai sử dụng uwsgi
Patrick

2
Hm, không phù hợp với tôi với django 2. Nếu tôi nhập trực tiếp mô hình sẵn sàng - tất cả đều tốt. Nếu tôi nhập khẩu mô hình trong tín hiệu và tín hiệu nhập khẩu sẵn sàng tôi nhận được một lỗi doesn't declare an explicit app_label..
Aldarund

@Aldarun, bạn có thể thử đặt 'my_app.apps.MyAppConfig' bên trong INSTALLED_APPS.
Ramil Aglyautdinov

26

Để giải quyết vấn đề của bạn, bạn chỉ cần nhập signal.py sau khi xác định mô hình của mình. Đó là tất cả.


2
Điều này cho đến nay là dễ dàng nhất và tôi không biết điều này sẽ hoạt động mà không có sự phụ thuộc theo chu kỳ. Cảm ơn!
bradenm

2
Xuất sắc. Thích cái này hơn câu trả lời của tôi. Mặc dù tôi không thực sự hiểu làm thế nào mà nó không gây ra một khẩu tròn ...
yprez

giải pháp không hoạt động với plugin autopep8 được bật trong Eclipse.
ramusus

5

Tôi cũng đặt các tín hiệu trong tệp signal.py và cũng có đoạn mã này tải tất cả các tín hiệu:

# import this in url.py file !

import logging

from importlib import import_module

from django.conf import settings

logger = logging.getLogger(__name__)

signal_modules = {}

for app in settings.INSTALLED_APPS:
    signals_module = '%s.signals' % app
    try:
        logger.debug('loading "%s" ..' % signals_module)
        signal_modules[app] = import_module(signals_module)
    except ImportError as e:
        logger.warning(
            'failed to import "%s", reason: %s' % (signals_module, str(e)))

Đây là dành cho dự án, tôi không chắc liệu nó có hoạt động ở cấp ứng dụng hay không.


Đây là giải pháp ưa thích của tôi cho đến nay vì nó phù hợp với các mô hình khác (như tasks.py)
dalore

1
Tìm thấy một vấn đề với thế này, nếu bạn bắt đầu shell các urls.py không được nhập khẩu và tín hiệu của bạn sẽ không đính kèm
dalore

vâng, câu trả lời của tôi hơi lỗi thời, có vẻ như django có lớp AppConfig ngày nay. Lần trước tôi đã sử dụng django, đó là phiên bản 1.3. Đề nghị điều tra xung quanh nó.
aisbaa

1
chúng tôi vẫn là 1.6 và vì vậy tôi đã phải chuyển tất cả các signal.py của chúng tôi sang các mô hình nếu không các lệnh quản lý và cần tây không được chọn
dalore

5

Trong các phiên bản Django cũ sẽ tốt hơn nếu đặt các tín hiệu trên __init__.pyhoặc có thể trong models.py(mặc dù ở các mô hình cuối cùng sẽ có kích thước lớn theo sở thích của tôi).

Với Django 1.9, tôi nghĩ tốt hơn là nên đặt các tín hiệu vào một signals.pytệp và nhập chúng với apps.py, nơi chúng sẽ được tải sau khi tải mô hình.

apps.py:

from django.apps import AppConfig


class PollsConfig(AppConfig):
    name = 'polls'

    def ready(self):
        from . import signals  # NOQA

Bạn cũng có thể phân chia tín hiệu của mình trên signals.pyhandlers.pytrong một thư mục khác trong mô hình có tên của bạn signals, nhưng đối với tôi đó chỉ là kỹ thuật. Hãy xem Đặt tín hiệu


3

Tôi đoán rằng bạn đang làm điều đó để các tín hiệu của bạn được đăng ký, để chúng được tìm thấy ở đâu đó. Tôi chỉ đặt các tín hiệu của mình ngay trong tệp models.py một cách bình thường.


vâng khi tôi di chuyển tín hiệu bên trong tệp mô hình, nó giải quyết được vấn đề. Nhưng tệp model.py của tôi khá lớn với tất cả các lớp, trình quản lý và quy tắc mô hình.
Mo J. Mughrabi

1
Theo kinh nghiệm của tôi, các nhà quản lý đã dễ dàng hơn một chút. Managers.py ftw.
Issac Kelly,

3

Điều này chỉ áp dụng nếu bạn có các tín hiệu của mình trong một signals.pytệp riêng biệt

Hoàn toàn đồng ý với câu trả lời của @EricMarcos nhưng cần nói rõ rằng tài liệu django đưa ra lời khuyên rõ ràng là không sử dụng biến default_app_config (mặc dù nó không sai). Đối với các phiên bản hiện tại, cách đúng sẽ là:

my_app / apps.py

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'my_app'

    def ready(self):
        import my_app.signals

settings.py

(Đảm bảo rằng bạn không chỉ có tên ứng dụng của mình trong các ứng dụng đã cài đặt mà thay vào đó là đường dẫn liên quan đến AppConfig của bạn)

INSTALLED_APPS = [
    'my_app.apps.MyAppConfig',
    # ...
]

1

Một giải pháp thay thế là nhập các hàm gọi lại từ signals.pyvà kết nối chúng trong models.py:

signal.py

def pre_save_callback_function(sender, instance, **kwargs):
    # Do stuff here

model.py

# Your imports here
from django.db.models.signals import pre_save
from yourapp.signals import pre_save_callback_function

class YourModel:
    # Model stuff here
pre_save.connect(pre_save_callback_function, sender=YourModel)

Ps: Nhập YourModelvào signals.pysẽ tạo ra một đệ quy; sử dụng sender, thay thế.

Ps2: Lưu lại instance trong hàm callback sẽ tạo một đệ quy. Bạn có thể tạo đối số điều khiển trong .savephương thức để điều khiển nó.

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.