양식의 clean () 메서드에서 요청 객체 또는 다른 변수에 어떻게 액세스합니까?


99

양식의 정리 메서드에 대한 request.user를 시도하고 있지만 요청 개체에 어떻게 액세스 할 수 있습니까? 변수 입력을 허용하도록 clean 메서드를 수정할 수 있습니까?

답변:


157

Ber의 대답 (threadlocals에 저장)은 매우 나쁜 생각입니다. 이런 식으로 할 이유가 전혀 없습니다.

훨씬 더 좋은 방법은 __init__추가 키워드 인수를 사용하도록 양식의 메서드를 재정의하는 것 request입니다. 이것은의 요청에 저장 형태 가 필요한 것, 당신이 당신의 청소 방법에 액세스 할 수 있습니다 곳에서.

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)

4
이 경우 당신이 옳습니다. 그러나 이것에서 Forms / Views를 수정하는 것은 바람직하지 않을 수 있습니다. 또한 메서드 매개 변수 또는 인스턴스 변수를 추가 할 수없는 스레드 로컬 저장소에 대한 사용 사례가 있습니다. 요청 데이터에 액세스해야하는 쿼리 필터에 대한 호출 가능한 인수에 대해 생각해보십시오. 호출에 매개 변수를 추가 할 수 없으며 참조 할 인스턴스도 없습니다.
Ber

4
요청 var를 전달하는 양식을 초기화 할 수 있기 때문에 관리 양식을 확장 할 때는 유용하지 않습니다. 어떤 아이디어?
Mordi

13
스레드 로컬 저장소를 사용하는 것이 왜 매우 나쁜 생각이라고 말합니까? 모든 곳에서 요청을 전달하기 위해 코드를 삭제하지 않아도됩니다.
Michael Mior 2011

9
요청 개체 자체를 양식에 전달하지 않고 필요한 요청 필드 (예 : 사용자)를 전달하지 않습니다. 그렇지 않으면 양식 논리를 요청 / 응답주기에 연결하여 테스트를 더 어렵게 만듭니다.
Andrew Ingram

2
크리스 프랫 admin.ModelAdmin의 형태를 처리 할 때를 위해, 너무 좋은 솔루션을 제공
radtek

34

UPDATED 10/25/2011 : Django 1.3이 그렇지 않으면 이상한 점을 표시하기 때문에 메서드 대신 동적으로 생성 된 클래스와 함께 이것을 사용하고 있습니다.

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

그런 다음 다음 MyCustomForm.__init__과 같이 재정의 합니다.

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

그런 다음 ModelFormwith의 모든 메소드에서 요청 객체에 액세스 할 수 있습니다 self.request.


1
Chris, "def __init __ (self, request = None, * args, ** kwargs)"는 첫 번째 위치 인수와 kwargs 모두에서 요청으로 끝날 것이기 때문에 좋지 않습니다. 나는 그것을 "def __init __ (self, * args, ** kwargs)"로 변경했고 작동합니다.
slinkp

1
이런. 그건 제 실수 였어요. 다른 업데이트를 할 때 코드의 해당 부분을 업데이트하는 것을 무시했습니다. 잡아 주셔서 감사합니다. 업데이트되었습니다.
Chris Pratt 2011

4
이것이 정말 메타 클래스입니까? 나는 그것이 정상적인 재정의라고 생각하며, __new__나중에 클래스의 __init__메서드에 전달되는의 kwargs 에 요청을 추가 합니다. 클래스 이름을 지정하는 ModelFormWithRequest것보다 의미가 훨씬 더 명확하다고 생각 ModelFormMetaClass합니다.
k4ml

2
이것은 메타 클래스가 아닙니다! stackoverflow.com/questions/100003/…
frnhr

32

기능 기반 뷰 대신 클래스 기반 뷰를 사용하는 경우 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)

1
이것은 나를 위해 일했습니다. 복잡한 WizardForm 논리로 인해 어쨌든 get_form_kwargs를 사용했기 때문에 메모를 작성했습니다. WizardForm에 대한 다른 답변은 없습니다.
datakid

2
나 외에 다른 사람이 이것이 웹 프레임 워크에 대해 매우 기초적인 일을하는 데 큰 엉망이라고 생각합니까? Django는 훌륭하지만 CBV를 전혀 사용하고 싶지 않습니다.
trpt4him

1
IMHO CBV의 이점은 FBV의 단점보다 훨씬 큽니다. 특히 100 % 단위 테스트 범위를 목표로하는 코드를 작성하는 25 명 이상의 개발자가있는 대규모 프로젝트에서 작업하는 경우 더욱 그렇습니다. 최신 버전의 Django가 request객체를 get_form_kwargs자동으로 포함 하는지 여부는 확실하지 않습니다 .
Joseph Victor Zammit

비슷한 맥락에서 get_form_kwargs에서 개체 인스턴스의 ID에 액세스 할 수있는 방법이 있습니까?
Hassan Baig

1
@HassanBaig 아마도 사용 self.get_object합니까? 는 CreateView을 확장합니다 SingleObjectMixin. 그러나 이것이 작동하는지 아니면 예외가 발생하는지 여부는 새 객체를 생성하는지 또는 기존 객체를 업데이트하는지에 따라 다릅니다. 즉, 두 경우 모두 테스트 (물론 삭제).
Joseph Victor Zammit

17

일반적인 방법은 미들웨어를 사용하여 스레드 로컬 참조에 요청 객체를 저장하는 것입니다. 그런 다음 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 문서에 설명 된 대로이 미들웨어를 등록하십시오.


2
위의 설명에도 불구하고이 방법은 작동하지만 다른 방법은 작동하지 않습니다. init 에서 양식 객체의 속성을 설정하는 것은 깨끗한 메소드로 안정적으로 전달되지 않는 반면 스레드 로컬을 설정하면이 데이터가 전달 될 수 있습니다.
rplevy

4
@rplevy 양식의 인스턴스를 만들 때 실제로 요청 객체를 전달 했습니까? 키워드 인수를 사용하는 것을 알아 차리지 못한 경우 **kwargs요청 객체를 MyForm(request.POST, request=request).
unode 2011 년

13

Django 관리자의 경우 Django 1.8에서

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

1
위의 최고 등급 방법은 실제로 Django 1.6과 1.9 사이에서 작동하지 않는 것 같습니다. 이것은 작동하고 훨씬 더 짧습니다. 감사!
Raik

9

관리자를 사용자 정의 할 때이 특정 문제가 발생했습니다. 특정 관리자의 자격 증명을 기반으로 특정 필드의 유효성을 검사하기를 원했습니다.

요청을 양식에 대한 인수로 전달하도록보기를 수정하고 싶지 않았기 때문에 다음과 같이했습니다.

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

감사합니다. 빠른 오타 : 11 행이 obj=obj아닙니다 obj=None.
François Constant

정말 좋은 대답입니다. 정말 좋아요!
누가 복음 DUPIN

Django 1.9는 다음을 제공 'function' object has no attribute 'base_fields'합니다. 그러나 더 간단한 (닫지 않고) @ François 대답은 원활하게 작동합니다.
raratiru

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")

이것은 때때로 정말 좋은 해결책입니다. get_form_class요청에 대해 많은 일을해야한다는 것을 안다면 CBV 방법 으로이 작업을 자주 수행합니다 . 클래스를 반복적으로 생성하는 데 약간의 오버 헤드가있을 수 있지만 가져 오기 시간에서 런타임으로 이동합니다.
매튜 Schinckel

5

Daniel Roseman 의 대답 은 여전히 ​​최고입니다. 그러나 몇 가지 이유로 키워드 인수 대신 요청에 첫 번째 위치 인수를 사용합니다.

  1. 같은 이름의 kwarg를 재정의 할 위험이 없습니다.
  2. 요청은 선택 사항이며 옳지 않습니다. 이 컨텍스트에서 요청 속성은 None이 아니어야합니다.
  3. args와 kwargs를 수정하지 않고도 부모 클래스에 깔끔하게 전달할 수 있습니다.

마지막으로 기존 변수를 덮어 쓰지 않도록보다 고유 한 이름을 사용합니다. 따라서 내 수정 된 대답은 다음과 같습니다.

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)

이제 form.py의 깨끗한 메소드에서 self.instance에 액세스 할 수 있습니다.


0

CreateView알아야 할 작은 트릭이있는 것처럼 "준비된"Django 클래스 뷰를 통해 액세스하려고 할 때 (= 공식 솔루션은 즉시 작동하지 않습니다). 다음 CreateView 과 같은 코드를 직접 추가해야합니다.

class MyCreateView(LoginRequiredMixin, CreateView):
    form_class = MyOwnForm
    template_name = 'my_sample_create.html'

    def get_form_kwargs(self):
        result = super().get_form_kwargs()
        result['request'] = self.request
        return result

= 간단히 말해 이것은 requestDjango의 Create / Update 뷰를 사용하여 양식 에 전달하는 솔루션 입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.