【问题标题】:Setting variables with Django signals without using globals使用 Django 信号设置变量而不使用全局变量
【发布时间】:2016-06-08 09:55:07
【问题描述】:

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

这是我在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

重要修改:

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

【问题讨论】:

  • ekhm... 更改表(结构)或更改数据?以及为什么需要在数据更改后重建表单?
  • 这将因带有多个线程或进程的 bing bang 失败......也就是生产部署:(
  • 假设我有一个选择小部件,其中包含来自表格列的所有不同选项。如果添加了某些内容,我希望这反映在下一次加载表单时,但由于数据很少更改,我想缓存表单。
  • 你能解释一下吗?或者指出资源是否已经解释?
  • 所以你使用分布式缓存(memcache.redis)并在那里存储一个 list 选项,然后在“save”或信号处理程序中更新外部缓存,你可以不触摸表单的定义,您只需从表单的 init 中的缓存加载数据

标签: python django django-signals


【解决方案1】:

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

相反,将用于构建表单的查询存储在共享缓存中,例如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())

但是,如果查询的开销足以证明增加的复杂性是合理的,我只会考虑这种解决方案。

【讨论】:

  • 我真的不明白为什么需要这样做,主要是因为这段代码在多个线程/进程中运行时会失败,更不用说多台服务器了
  • @Jerzyk 你是对的!我想一个正确的解决方案是将查询结果缓存在共享缓存中。将更新我的答案
  • 自从我发现“我们建议不要将文字值 None 存储在缓存中,因为您将无法区分存储的 None 值和返回值 None 表示的缓存未命中。 "在docs.djangoproject.com/en/1.9/topics/cache/… 中,我可能更喜欢cache.set(CHOICES_CACHE_KEY, choices, 60 * 60 * 24 * 365)。否则很好的答案。
  • 指的是传递 None 作为值,即cache.set(CHOICES_CACHE_KEY, None, 3600)。通过 None 作为超时很好,即cache.set(CHOICES_CACHE_KEY, choices, None)。在文档中查看引用上方的两段
猜你喜欢
  • 2018-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-03
  • 1970-01-01
  • 1970-01-01
  • 2015-05-01
  • 1970-01-01
相关资源
最近更新 更多