在Django表单中缓存ModelChoiceField或ModelMultipleChoiceField的查询集选项

str*_*pe7 12 django django-forms django-admin django-cache django-queryset

在Django表单中使用ModelChoiceFieldModelMultipleChoiceField时,有没有办法传入一组缓存的选项?目前,如果我通过queryset参数指定选项,则会导致数据库命中.

我想使用memcached缓存这些选项,并在显示具有此类字段的表单时防止对数据库的不必要的命中.

小智 13

您可以在QuerySet中覆盖"all"方法

from django.db import models
class AllMethodCachingQueryset(models.query.QuerySet):
    def all(self, get_from_cache=True):
        if get_from_cache:
            return self
        else:
            return self._clone()


class AllMethodCachingManager(models.Manager):
    def get_query_set(self):
        return AllMethodCachingQueryset(self.model, using=self._db)


class YourModel(models.Model):
    foo = models.ForeignKey(AnotherModel)

    cache_all_method = AllMethodCachingManager()
Run Code Online (Sandbox Code Playgroud)

然后在使用表单之前更改字段的查询集(例如,当您使用表单集时)

form_class.base_fields['foo'].queryset = YourModel.cache_all_method.all()
Run Code Online (Sandbox Code Playgroud)


Nic*_*s78 12

ModelChoiceField特别是在生成选择时创建命中的原因- 无论QuerySet是否先前已填充 - 都在此行中

for obj in self.queryset.all(): 
Run Code Online (Sandbox Code Playgroud)

django.forms.models.ModelChoiceIterator.关于QuerySets缓存Django文档突出显示,

可调用属性每次都会导致数据库查找.

所以我更愿意使用

for obj in self.queryset:
Run Code Online (Sandbox Code Playgroud)

即使我不是100%肯定这一切的含义(我知道我之后没有对查询集的大计划,所以我认为没有副本.all()创建我很好).我很想在源代码中改变这个,但是因为我将在下次安装时忘记它(并且它开始时风格很糟糕)我最终编写了我的自定义ModelChoiceField:

class MyModelChoiceIterator(forms.models.ModelChoiceIterator):
    """note that only line with # *** in it is actually changed"""
    def __init__(self, field):
        forms.models.ModelChoiceIterator.__init__(self, field)

    def __iter__(self):
        if self.field.empty_label is not None:
            yield (u"", self.field.empty_label)
        if self.field.cache_choices:
            if self.field.choice_cache is None:
                self.field.choice_cache = [
                    self.choice(obj) for obj in self.queryset.all()
                ]
            for choice in self.field.choice_cache:
                yield choice
        else:
            for obj in self.queryset: # ***
                yield self.choice(obj)


class MyModelChoiceField(forms.ModelChoiceField):
    """only purpose of this class is to call another ModelChoiceIterator"""
    def __init__(*args, **kwargs):
        forms.ModelChoiceField.__init__(*args, **kwargs)

    def _get_choices(self):
        if hasattr(self, '_choices'):
            return self._choices

        return MyModelChoiceIterator(self)

    choices = property(_get_choices, forms.ModelChoiceField._set_choices)
Run Code Online (Sandbox Code Playgroud)

这并不能解决数据库缓存的一般问题,但是因为你ModelChoiceField特别询问这一点,这正是让我首先考虑缓存的原因,认为这可能会有所帮助.