在不使用全局变量的情况下使用 Django 信号设置变量

Setting variables with Django signals without using globals

在 Django 1.9 中,我构建了一个基于数据库 table 的表单列表,对应于我 forms.py 中的 TheModel。当数据库 table 发生变化时,我希望表单也发生变化。因此我设置了一个 Django 信号来监听变化并更新表单。我现在的问题是信号处理程序无法真正返回任何内容,因此我需要将表单列表作为 global 变量访问。由于使用 global 通常被认为是不好的做法:是否有更好的解决方案,或者在这种情况下使用 global 被认为是 acceptable?

这是我在 views.py 中的代码:

from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete
from .forms import construct_forms

# Init container for forms.
forms = construct_forms()
# Be aware that this signal is firiing twice for some reason.
@receiver((post_save, post_delete), sender=TheModel, dispatch_uid='change')
def reconstruct_forms(sender, **kwargs):
    """
    Reconstruct forms if model was changed.
    """
    # Use global variable so `forms` will be available in 
    # updated version later on.
    global forms
    forms = construct_forms()
    print('Model was changed, forms are reconstructed.')

def some_view(request):
    # Do something with the forms

重要编辑:

当我问这个问题时,我完全不知道信号只在 运行 生产服务器的一个线程中工作。因此,像这样使用信号更新内存变量的方法最终将导致服务器上的一个线程显示更新的表单,而信号未到达的其余线程将显示过时的版本。当然,这在生产中是 unacceptable 。如果确实需要缓存,您应该查看有关缓存的 Django docs。对于我的小表格构造,它实际上是矫枉过正。我将把这个问题留给正确的方向。请不要尝试按照我的方式实施!

正如@Jerzyk 指出的那样,缓存表单只能在单线程环境中工作,并且很可能在生产中失败。信号仅在 save/delete 发生的线程中发送和处理。其他进程将不知道更改。

相反,将用于构建表单的查询存储在共享缓存中,例如memcached 或 redis,使用 Django's cache framework.

from django.core.cache import cache

CHOICES_CACHE_KEY = 'choice_cache_key'

def get_cached_choices():
    choices = cache.get(CHOICES_CACHE_KEY)
    if choices is None:
        choices = ...  # Query the DB here
        cache.set(CHOICES_CACHE_KEY, choices, None)  # None caches forever
    return choices

def construct_forms(choices):
    forms = ...  # build forms with choices
    return forms

@receiver((post_save, post_delete), sender=TheModel, dispatch_uid='change')
def clear_choices_cache(sender, **kwargs):
    cache.delete(CHOICES_CACHE_KEY)

def some_view(request):
    # Do something with the forms
    forms = construct_forms(get_cached_choices())

但是,只有在查询的开销足以证明增加的复杂性是合理的情况下,我才会考虑此解决方案。