Ленивый выбор в форме Django

У меня есть Django my_forms.py вот так:

class CarSearchForm(forms.Form):  
    # lots of fields like this
    bodystyle = forms.ChoiceField(choices=bodystyle_choices())  

Каждый выбор, например, («Седан», «Седан (15 машин)»). Таким образом, выбор вычисляется этой функцией.

def bodystyle_choices():  
    return [(bodystyle.bodystyle_name, '%s (%s cars)' %  
          (bodystyle.bodystyle_name, bodystyle.car_set.count()))  
          for bodystyle in Bodystyle.objects.all()]

Моя проблема в том, что функции выбора выполняются каждый раз, когда я просто импортирую my_forms.py. Я думаю, это связано с тем, как Django объявляет свои поля: в классе, но не в методе класса. Это нормально, но мой views.py импортирует my_forms.py, поэтому поиск вариантов выполняется по каждому запросу, независимо от того, какое представление используется.

I thought that maybe putting choices=bodystyle_choices with no bracket would work, but I get:

'function' object is not iterable

Очевидно, я могу использовать кеширование и поместить «import my_forms» только в требуемые функции просмотра, но это не меняет сути: мой выбор должен быть ленивым!


person Tom Viner    schedule 20.02.2009    source источник


Ответы (4)


Можно воспользоваться функцией "ленивый" :)

from django.utils.functional import lazy

class CarSearchForm(forms.Form):  
    # lots of fields like this
    bodystyle = forms.ChoiceField(choices=lazy(bodystyle_choices, tuple)())

очень хорошая служебная функция!

person Sidi    schedule 10.05.2009
comment
Определенно лучшее решение, это должен быть принятый ответ imo. - person Sverre Rabbelier; 27.02.2011
comment
/ Согласен - это самое чистое решение, которое я видел до сих пор, и оно позволяет вам пропустить проблемы с проверками, что является важным отличием от ModelChoiceField. - person Hassek; 19.08.2011
comment
Это не работает, по крайней мере, с Django 1.6, потому что ChoiceField._set_choices делает self._choices = self.widget.choices = list(value) - person spookylukey; 23.10.2014
comment
У меня это вообще не работает на Django 1.7, может ли кто-нибудь проверить, действительно ли это работает? Во-первых, он вызывает исключение о неправильном типе возвращаемого значения (ленивый объект возвратил неожиданный тип), когда я использую «кортеж» в качестве второго параметра для «ленивый». Во-вторых, когда я использую там «список», функция вызывается ... один раз! Потом он больше не вызывается, и у меня тот же список значений, что и в начале. - person dotz; 30.05.2015

Попробуйте использовать ModelChoiceField вместо простого ChoiceField. Я думаю, вы сможете добиться желаемого, немного подправив свои модели. Дополнительные сведения см. В документации.

Я бы также добавил, что ModelChoiceFields по умолчанию lazy :)

person Baishampayan Ghose    schedule 20.02.2009

Расширяя сказанное Байшампаяном Гхошем, это, вероятно, следует рассматривать как наиболее прямой подход:

from django.forms import ModelChoiceField

class BodystyleChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return '%s (%s cars)' % (obj.bodystyle_name, obj.car_set.count()))

class CarSearchForm(forms.Form):  
    bodystyle = BodystyleChoiceField(queryset=Bodystyle.objects.all())

Документы находятся здесь: https://docs.djangoproject.com/en/1.8/ref/forms/fields/#modelchoicefield

Это имеет то преимущество, что form.cleaned_data['bodystyle'] является экземпляром Bodystyle вместо строки.

person John    schedule 08.04.2015

Теперь вы можете просто использовать (поскольку я думаю, что Django 1.8):

class CarSearchForm(forms.Form):  
    # lots of fields like this
    bodystyle = forms.ChoiceField(choices=bodystyle_choices)  

Обратите внимание на недостающую круглую скобку. Если вам нужно передать аргументы, я просто делаю специальную версию функции с жестко запрограммированными только для этой формы.

person MrDBA    schedule 27.03.2019