Django: заставить ModelChoiceField оценивать набор запросов во время выполнения

Я переопределил диспетчер моих моделей по умолчанию, чтобы отображать только разрешенные элементы в соответствии с зарегистрированным пользователем (своего рода объектное разрешение):

class User_manager(models.Manager):
    def get_query_set(self):
        """ Filter results according to logged user """
        #Compose a filter dictionary with current user (stored in a middleware method)
        user_filter = middleware.get_user_filter() 
        return super(User_manager, self).get_query_set().filter(**user_filter)

class Foo(models.Model):
    objects = User_manager()
    ...

Таким образом, всякий раз, когда я использую Foo.objects, текущий пользователь извлекается, и фильтр применяется к набору запросов по умолчанию, чтобы отображать только разрешенные записи.

Затем у меня есть модель с ForeignKey для Foo:

class Bar(models.Model):
    foo = models.ForeignKey(Foo)

class BarForm(form.ModelForm):
    class Meta:
        model = Bar

Когда я составляю BarForm, я ожидаю увидеть только экземпляры filteres Foo, но фильтр не применяется. Я думаю, это потому, что набор запросов оценивается и кэшируется при запуске Django, когда ни один пользователь не зарегистрирован и фильтр не применяется.

Есть ли способ заставить Django оценивать набор запросов ModelChoice во время выполнения, не делая его явным в определении формы? (несмотря на все проблемы с производительностью...)

EDIT Я нашел, где оценивается набор запросов (django\db\models\fields\related.py: 887):

def formfield(self, **kwargs):
    db = kwargs.pop('using', None)
    defaults = {
        'form_class': forms.ModelChoiceField,
        'queryset': self.rel.to._default_manager.using(db).complex_filter(self.rel.limit_choices_to),
        'to_field_name': self.rel.field_name,
    }
    defaults.update(kwargs)
    return super(ForeignKey, self).formfield(**defaults)

Любой намек?


person Don    schedule 18.03.2011    source источник
comment
Наборы запросов ModelChoice уже ленивы, возможно, вы где-то оцениваете набор запросов, показ некоторого кода (менеджер + реализация формы) поможет :)   -  person Tommaso Barbugli    schedule 18.03.2011
comment
Я добавил код. Поставив точку останова внутри пользовательского менеджера, я вижу, что get_queryest оценивается только при запуске Django (когда в «runserver» отображается сообщение «Проверка моделей...»)   -  person Don    schedule 18.03.2011


Ответы (3)


я использую init пользовательской формы:

class BT_Form(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BT_Form, self).__init__(*args, **kwargs)

        #prepare new values
        cities = [(u'',u'------')] #default value
        cities.extend([
            (
                c.pk, 
                c.__unicode__()
            ) for c in City.objects.filter(enabled=True).all()
        ])

        self.fields['fly_from_city'].choices = cities #renew values
person fun_vit    schedule 08.11.2011

Была именно эта проблема - нужно было заполнить форму выбора пользовательскими объектами из группы, но ответ fun_vit неверен (по крайней мере, для django 1.5)

Во-первых, вы не хотите перезаписывать объект field['somefield'].choices — это объект ModelChoiceIterator, а не набор запросов. Во-вторых, комментарий в django.forms.BaseForm предостерегает вас от переопределения base_fields:

    # The base_fields class attribute is the *class-wide* definition of
    # fields. Because a particular *instance* of the class might want to
    # alter self.fields, we create self.fields here by copying base_fields.
    # Instances should always modify self.fields; they should not modify
    # self.base_fields.

Это сработало для меня (django 1.5):

class MyForm(ModelForm):
    users = ModelMultipleChoiceField(queryset=User.objects.none())

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args,**kwargs)

        site = Site.objects.get_current()
        self.fields['users'].queryset = site.user_group.user_set.all()

    class Meta:
        model = MyModel
person wjin    schedule 29.09.2013
comment
обратите внимание, вы также можете заменить весь объект self.fields['users'], создав новый объект поля. - person wjin; 30.09.2013

Ни в коем случае: мне пришлось переписать определение набора запросов (которое оценивается при запуске)

person Don    schedule 22.04.2011