Вопрос по python – Как мне получить доступ к объекту запроса или любой другой переменной в методе clean () формы?

86

Я пытаюсь запросить .user для чистого метода формы, но как я могу получить доступ к объекту запроса? Могу ли я изменить метод clean, чтобы разрешить ввод переменных?

Ваш Ответ

10   ответов
5

Даниэль Роузман все еще лучший. Однако я бы использовал первый позиционный аргумент для запроса вместо ключевого аргумента по нескольким причинам:

You don't run the risk of overriding a kwarg with the same name The request is optional which is not right. The request attribute should never be None in this context. You can cleanly pass the args and kwargs to the parent class without having to modify them.

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

class MyForm(forms.Form):

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


  def clean(self):
      ... access the request object via self._my_request ...
3

согласно вашему требованию, вы хотите получить доступ к пользователю в чистом методе формы. Вы можете попробовать это. View.py

person=User.objects.get(id=person_id)
form=MyForm(request.POST,instance=person)

forms.py

def __init__(self,*arg,**kwargs):
    self.instance=kwargs.get('instance',None)
    if kwargs['instance'] is not None:
        del kwargs['instance']
    super(Myform, self).__init__(*args, **kwargs)

Теперь вы можете получить доступ к self.instance любым чистым методом в form.py

10

class MyModelAdmin(admin.ModelAdmin):
    ...
    form = RedirectForm

    def get_form(self, request, obj=None, **kwargs):
        form = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        form.request = request
        return form
Похоже, что метод с самым высоким рейтингом выше действительно перестал работать где-то между Django 1.6 и 1.9. Этот действительно работает и намного короче. Спасибо!
Работает плавно на 1.9, спасибо!
5

вероятно, плохая практика), но если вы используете форму только в одном представлении, вы можете ограничить ее внутри самого метода представления.

def my_view(request):

    class ResetForm(forms.Form):
        password = forms.CharField(required=True, widget=forms.PasswordInput())

        def clean_password(self):
            data = self.cleaned_data['password']
            if not request.user.check_password(data):
                raise forms.ValidationError("The password entered does not match your account password.")
            return data

    if request.method == 'POST':
        form = ResetForm(request.POST, request.FILES)
        if form.is_valid():

            return HttpResponseRedirect("/")
    else:
        form = ResetForm()

    return render_to_response(request, "reset.html")
Иногда это действительно хорошее решение: я часто делаю это в CBVget_form_class метод, если я знаю, мне нужно сделать много вещей с запросом. При повторном создании класса могут быть некоторые издержки, но это просто перемещает его со времени импорта на время выполнения.
16

ылке потока с использованием промежуточного программного обеспечения. Затем вы можете получить к нему доступ из любого места вашего приложения, включая метод Form.clean ().

Изменение подписи метода Form.clean () означает, что у вас есть собственная модифицированная версия Django, которая может не соответствовать вашей.

Спасибо, подсчет промежуточного программного обеспечения выглядит примерно так:

import threading
_thread_locals = threading.local()

def get_current_request():
    return getattr(_thread_locals, 'request', None)

class ThreadLocals(object):
    """
    Middleware that gets various objects from the
    request object and saves them in thread local storage.
    """
    def process_request(self, request):
        _thread_locals.request = request

Зарегистрируйте это промежуточное ПО, как описано вDjango Docs

Несмотря на вышеприведенные комментарии, этот метод работает, а другой - нет. Установка атрибута объекта формы вinit не надежно переносится на чистые методы, тогда как установка локальных потоков позволяет переносить эти данные.
@rplevy действительно ли вы передали объект запроса при создании экземпляра формы? В случае, если вы не заметили, он использует ключевые аргументы**kwargsЭто означает, что вам нужно будет передать объект запроса какMyForm(request.POST, request=request).
144

бсолютно никакой причины делать это таким образом.

Гораздо лучший способ - переопределить форму__init__ метод для получения дополнительного ключевого аргумента,request, Это сохраняет запрос вformгде это требуется и откуда вы можете получить к нему доступ чистым способом.

class MyForm(forms.Form):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyForm, self).__init__(*args, **kwargs)


    def clean(self):
        ... access the request object via self.request ...

и по вашему мнению:

myform = MyForm(request.POST, request=request)
Я бы не передавал сам объект запроса в форму, а использовал бы поля запроса, которые вам нужны (то есть пользователя), иначе вы привязываете логику формы к циклу запрос / ответ, что усложняет тестирование.
Это бесполезно, когда вы расширяете форму администратора, потому что вы можете инициировать ее, передав запрос var. Любая идея & # xBF ;?
У Криса Пратта тоже есть хорошее решение для работы с формами в admin.ModelAdmin
Вы правы в этом деле. Однако может быть нежелательно, чтобы измененные формы / представления в этом были. Кроме того, существуют варианты использования для локального хранилища потока, где невозможно добавить параметры метода или переменные экземпляра. Подумайте о вызываемом аргументе для фильтра запросов, которому необходим доступ к данным запроса. Вы не можете ни добавить параметр к вызову, ни один экземпляр для ссылки.
Почему вы говорите, что использование локального хранилища потоков - очень плохая идея? Это позволяет избежать необходимости отбрасывать код для передачи запроса везде.
7

Я хотел, чтобы определенное поле было проверено на основе учетных данных конкретного администратора.

Поскольку я не хотел изменять представление для передачи запроса в качестве аргумента в форму, я сделал следующее:

class MyCustomForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def clean(self):
        # make use of self.request here

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj=obj, **kwargs)
        def form_wrapper(*args, **kwargs):
            a = ModelForm(*args, **kwargs)
            a.request = request
            return a
    return form_wrapper
Хорошо подмечено. Исправлено, спасибо
Как раз то, что мне было нужно!
Действительно хороший ответ, я люблю это!
Джанго 1.9 поставляет:'function' object has no attribute 'base_fields', Однако более простой (без замыкания) ответ @ Fran & # xE7; ois работает гладко.
Спасибо за это. Быстрая опечатка:obj=obj неobj=None на линии 11.
26

Class Based Viewsвместо представлений на основе функций переопределитеget_form_kwargs в вашем редактировании. Пример кода для кастомаCreateView:

from braces.views import LoginRequiredMixin

class MyModelCreateView(LoginRequiredMixin, CreateView):
    template_name = 'example/create.html'
    model = MyModel
    form_class = MyModelForm
    success_message = "%(my_object)s added to your site."

    def get_form_kwargs(self):
        kw = super(MyModelCreateView, self).get_form_kwargs()
        kw['request'] = self.request # the trick!
        return kw

    def form_valid(self):
        # do something

Выше приведенный код сделаетrequest доступны в качестве одного из ключевых аргументов формы__init__ функция конструктора. Поэтому в вашемModelForm делать:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        # important to "pop" added kwarg before call to parent's constructor
        self.request = kwargs.pop('request')
        super(MyModelForm, self).__init__(*args, **kwargs)
Аналогичным образом, есть ли способ получить доступ к идентификатору экземпляра объекта в get_form_kwargs?
Это сработало для меня. Я делаю заметку, потому что я все равно использовал get_form_kwargs из-за сложной логики WizardForm. Никакого другого ответа, который я видел, не было связано с WizardForm.
Кто-нибудь кроме меня думает, что это просто большой беспорядок, чтобы делать что-то, что довольно элементарно для веб-фреймворка? Джанго великолепен, но это заставляет меня вообще не хотеть использовать CBV.
@HassanBaig Возможно использованиеself.get_object?CreateView расширяетSingleObjectMixin, Но работает ли это или вызывает исключение, зависит от того, создаете ли вы новый объект или обновляете существующий; то есть проверить оба случая (и, конечно, удаление).
ИМХО, преимущества CBV намного перевешивают недостатки FBV, особенно если вы работаете над большим проектом с написанием кода более 25 разработчиков, который нацелен на 100% покрытие модульных тестов. Не уверен, что более новые версии Django обслуживаютrequest объект внутриget_form_kwargs автоматически.
30

UPDATED 10/25/2011Я сейчас использую это с метаклассом вместо метода, так как Django 1.3 отображает некоторые странности в противном случае.

    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
        class ModelFormMetaClass(ModelForm):
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return ModelForm(*args, **kwargs)
        return ModelFormMetaClass

Затем переопределитьMyCustomForm.__init__ следующее:

class MyCustomForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyCustomForm, self).__init__(*args, **kwargs)

Затем вы можете получить доступ к объекту запроса из любого методаModelForm сself.request.

Это действительно метакласс? Я думаю, что это просто нормальное переопределение, вы добавляете запрос в__new__kwargs, которые позже будут переданы классу__init__ метод. Наименование классаModelFormWithRequest Я думаю, что в этом смысл намного яснее, чемModelFormMetaClass.
Упс. Это была просто ошибка с моей стороны. Я забыл обновить эту часть кода, когда сделал другое обновление. Спасибо за улов. Обновлено.
Это НЕ метакласс! Увидетьstackoverflow.com/questions/100003/…
Крис, это & quot; def __init __ (self, request = None, * args, ** kwargs) & quot; это плохо, потому что в конечном итоге запрос будет как в первом позиционном аргументе, так и в kwargs. Я изменил его на & quot; def __init __ (self, * args, ** kwargs) & quot; и это работает.
3

Похожие вопросы