source

Django 사용자 지정 양식 매개 변수를 양식 세트로 전달

manycodes 2023. 6. 15. 21:57
반응형

Django 사용자 지정 양식 매개 변수를 양식 세트로 전달

이것은 장고 1.9에서 form_kwargs로 수정되었습니다.

다음과 같은 장고 양식이 있습니다.

class ServiceForm(forms.Form):
    option = forms.ModelChoiceField(queryset=ServiceOption.objects.none())
    rate = forms.DecimalField(widget=custom_widgets.SmallField())
    units = forms.IntegerField(min_value=1, widget=custom_widgets.SmallField())

    def __init__(self, *args, **kwargs):
        affiliate = kwargs.pop('affiliate')
        super(ServiceForm, self).__init__(*args, **kwargs)
        self.fields["option"].queryset = ServiceOption.objects.filter(affiliate=affiliate)

저는 이 양식을 다음과 같은 것으로 부릅니다.

form = ServiceForm(affiliate=request.affiliate)

에▁where디request.affiliate로그인한 사용자입니다.이것은 의도한 대로 작동합니다.

제 문제는 제가 이제 이 단일 양식을 양식 세트로 바꾸고 싶어한다는 것입니다.양식 세트를 만들 때 제휴 정보를 개별 양식에 전달하는 방법을 알 수 없습니다.이것으로 양식을 만드는 문서에 따르면, 저는 다음과 같은 것을 해야 합니다.

ServiceFormSet = forms.formsets.formset_factory(ServiceForm, extra=3)

그런 다음 다음 다음과 같이 생성해야 합니다.

formset = ServiceFormSet()

이제 어떻게 하면 apparity=request.dll을 개별 양식에 전달할 수 있습니까?

공식 문서 작성 방법

장고 2.0:

ArticleFormSet = formset_factory(MyArticleForm)
formset = ArticleFormSet(form_kwargs={'user': request.user})

https://docs.djangoproject.com/en/2.0/topics/forms/formsets/ #messages-custom-parameters-to-formset-forms

funtools.partial funtools를 사용할 것입니다.:

from functools import partial, wraps
from django.forms.formsets import formset_factory

ServiceFormSet = formset_factory(wraps(ServiceForm)(partial(ServiceForm, affiliate=request.affiliate)), extra=3)

저는 이것이 가장 깨끗한 접근 방식이며, 서비스 양식에 어떤 영향도 미치지 않는다고 생각합니다(즉, 하위 분류를 어렵게 함).

양식 클래스를 함수에 동적으로 구축하여 마감을 통해 제휴사에 액세스할 수 있도록 합니다.

def make_service_form(affiliate):
    class ServiceForm(forms.Form):
        option = forms.ModelChoiceField(
                queryset=ServiceOption.objects.filter(affiliate=affiliate))
        rate = forms.DecimalField(widget=custom_widgets.SmallField())
        units = forms.IntegerField(min_value=1, 
                widget=custom_widgets.SmallField())
    return ServiceForm

추가로 옵션 필드의 쿼리 집합을 다시 작성할 필요가 없습니다.단점은 하위 분류가 약간 펑키하다는 것입니다.(모든 하위 클래스는 유사한 방식으로 작성되어야 합니다.)

편집:

설명에 대한 응답으로 클래스 이름을 사용할 모든 위치에 대해 이 함수를 호출할 수 있습니다.

def view(request):
    affiliate = get_object_or_404(id=request.GET.get('id'))
    formset_cls = formset_factory(make_service_form(affiliate))
    formset = formset_cls(request.POST)
    ...

이것이 제게 효과가 있었던 장고 1.7:

from django.utils.functional import curry    

lols = {'lols':'lols'}
formset = modelformset_factory(MyModel, form=myForm, extra=0)
formset.form = staticmethod(curry(MyForm, lols=lols))
return formset

#form.py
class MyForm(forms.ModelForm):

    def __init__(self, lols, *args, **kwargs):

누군가에게 도움이 되길 바라며, 그것을 알아내는 데 충분히 오랜 시간이 걸렸습니다;)

나는 폐쇄 솔루션이 "더 깔끔하고" 더 파이썬적이기 때문에 좋지만(따라서 +1 tomar가 대답할 것이다), 장고 양식에는 양식 세트의 쿼리 세트를 필터링하는 데 사용할 수 있는 콜백 메커니즘도 있습니다.

그것은 또한 문서화되지 않았고, 나는 그것이 장고 개발자들이 그것을 별로 좋아하지 않을 수 있는 지표라고 생각합니다.

따라서 기본적으로 양식 세트를 동일하게 작성하고 콜백을 추가합니다.

ServiceFormSet = forms.formsets.formset_factory(
    ServiceForm, extra=3, formfield_callback=Callback('option', affiliate).cb)

다음과 같은 클래스의 인스턴스를 만듭니다.

class Callback(object):
    def __init__(self, field_name, aff):
        self._field_name = field_name
        self._aff = aff
    def cb(self, field, **kwargs):
        nf = field.formfield(**kwargs)
        if field.name == self._field_name:  # this is 'options' field
            nf.queryset = ServiceOption.objects.filter(affiliate=self._aff)
        return nf

이것은 당신에게 일반적인 아이디어를 줄 것입니다.이렇게 콜백을 객체 메소드로 만드는 것은 조금 더 복잡하지만 간단한 함수 콜백을 수행하는 것에 비해 조금 더 유연합니다.

저는 이것을 칼 마이어스의 답변에 대한 코멘트로 넣고 싶었지만, 포인트가 필요하기 때문에 그냥 여기에 놓았습니다.2시간이나 걸려서 알아냈으니 누군가에게 도움이 되었으면 좋겠습니다.

inlineformset_factory 사용에 대한 참고 사항입니다.

저는 이 솔루션을 직접 사용했고 inlineformset_factory와 함께 사용하기 전까지는 완벽하게 작동했습니다.나는 장고 1.0.2를 실행하고 있었는데 이상한 키 오류 예외가 발생했습니다.저는 최신 트렁크로 업그레이드했고 그것은 직접 작동했습니다.

이제 다음과 유사하게 사용할 수 있습니다.

BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
BookFormSet.form = staticmethod(curry(BookForm, user=request.user))

commit091c18f50266097f648efc7cac2503968e9d217 2012년 8월 14일 화요일 23:44:46 +0200 기준으로 승인된 솔루션은 더 이상 작동할 수 없습니다.

장고의 현재 버전.formes.modelform_factory 함수는 "유형 구성 기법"을 사용하며, 전달된 형식의 type() 함수를 호출하여 메타 클래스 유형을 얻은 다음, 결과를 사용하여 해당 유형의 클래스 객체를 즉시 구성합니다.

# Instatiate type(form) in order to use the same metaclass as form.
return type(form)(class_name, (form,), form_class_attrs)

이은심지도 합니다.curry편집자partial "물게 전달된 그것은 "리가당물하유도도 "의 변수를 입니다.ModelFormClass개체, 오류 메시지 반환:

function() argument 1 must be code, not str

첫 매개 변수로 하기 위해 했습니다. 이 는 다음과 같이 호출됩니다. 그런 다음 이 함수를 호출합니다.super.__init__나고끝 update제너레이터 기능의 호출에 따라 제공된 것과 함께 궤간을 울립니다.

def class_gen_with_kwarg(cls, **additionalkwargs):
  """class generator for subclasses with additional 'stored' parameters (in a closure)
     This is required to use a formset_factory with a form that need additional 
     initialization parameters (see http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset)
  """
  class ClassWithKwargs(cls):
      def __init__(self, *args, **kwargs):
          kwargs.update(additionalkwargs)
          super(ClassWithKwargs, self).__init__(*args, **kwargs)
  return ClassWithKwargs

그런 다음 코드에서 폼 팩토리를 다음과 같이 부릅니다.

MyFormSet = inlineformset_factory(ParentModel, Model,form = class_gen_with_kwarg(MyForm, user=self.request.user))

주의사항:

  • 이것은 최소한 현재로서는 거의 테스트를 받지 못했습니다.
  • 제공된 매개 변수가 충돌하고 생성자가 반환한 개체를 사용하는 코드에서 사용되는 매개 변수를 덮어쓸 수 있습니다.

칼 마이어의 해결책은 매우 우아해 보입니다.저는 모델 폼 세트용으로 구현을 시도했습니다.클래스 내에서 정적 메소드를 호출할 수 없다는 인상을 받았지만 다음은 설명할 수 없이 작동합니다.

class MyModel(models.Model):
  myField = models.CharField(max_length=10)

class MyForm(ModelForm):
  _request = None
  class Meta:
    model = MyModel

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

class MyFormsetBase(BaseModelFormSet):
  _request = None

def __init__(self,*args,**kwargs):
  self._request = kwargs.pop('request', None)
  subFormClass = self.form
  self.form = curry(subFormClass,request=self._request)
  super(MyFormsetBase,self).__init__(*args,**kwargs)

MyFormset =  modelformset_factory(MyModel,formset=MyFormsetBase,extra=1,max_num=10,can_delete=True)
MyFormset.form = staticmethod(curry(MyForm,request=MyFormsetBase._request))

제가 보기에, 만약 제가 이런 일을 한다면,

formset = MyFormset(request.POST,queryset=MyModel.objects.all(),request=request)

그러면 "request" 키워드가 양식 집합의 모든 구성원 양식으로 전파됩니다.기쁘지만, 왜 이것이 효과가 있는지 전혀 모르겠습니다. 잘못된 것 같습니다.좋은 의견이라도 있나?

저는 이 게시물을 보기 전에 이 문제를 해결하기 위해 시간을 보냈습니다.

제가 생각해낸 해결책은 클로저 솔루션(그리고 제가 이전에 장고 모델 양식과 함께 사용한 적이 있는 솔루션)이었습니다.

위에서 설명한 것처럼 카레() 방식을 시도해 보았지만 장고 1.0으로 작동하지 않아 결국 폐쇄 방식으로 되돌아갔습니다.

마감 방법은 매우 깔끔하며 클래스 정의가 뷰 또는 다른 함수 내부에 내포되어 있다는 것이 유일한 특이점입니다.저는 이것이 저에게 이상하게 보인다는 사실이 이전의 프로그래밍 경험과 단절된 것이라고 생각하고 더 역동적인 언어에 배경이 있는 누군가는 눈 하나 깜짝하지 않을 것이라고 생각합니다!

저도 비슷한 일을 해야 했습니다.이는 다음과 유사합니다.curry솔루션:

def form_with_my_variable(myvar):
   class MyForm(ServiceForm):
     def __init__(self, myvar=myvar, *args, **kwargs):
       super(SeriveForm, self).__init__(myvar=myvar, *args, **kwargs)
   return MyForm

factory = inlineformset_factory(..., form=form_with_my_variable(myvar), ... )

저는 여기 처음 와서 댓글을 달 수가 없어요.이 코드도 작동하기를 바랍니다.

ServiceFormSet = formset_factory(ServiceForm, extra=3)

ServiceFormSet.formset = staticmethod(curry(ServiceForm, affiliate=request.affiliate))

폼셋의 추가 매개 변수 추가에 대해서는BaseFormSet형식 대신에

답변을 바탕으로 보다 명확한 솔루션을 찾았습니다.

class ServiceForm(forms.Form):
    option = forms.ModelChoiceField(
            queryset=ServiceOption.objects.filter(affiliate=self.affiliate))
    rate = forms.DecimalField(widget=custom_widgets.SmallField())
    units = forms.IntegerField(min_value=1, 
            widget=custom_widgets.SmallField())

    @staticmethod
    def make_service_form(affiliate):
        self.affiliate = affiliate
        return ServiceForm

다음과 같은 뷰로 실행

formset_factory(form=ServiceForm.make_service_form(affiliate))

언급URL : https://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset

반응형