[python] Django가 사용자 정의 양식 매개 변수를 Formset에 전달

이것은 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)

request.affiliate로그인 한 사용자는 어디에 있습니까 ? 의도 한대로 작동합니다.

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

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

그런 다음 다음과 같이 만들어야합니다.

formset = ServiceFormSet()

이제 이런 식으로 계열사 = request.affiliate를 개별 양식에 어떻게 전달할 수 있습니까?



답변

내가 사용하는 것이 functools.partialfunctools.wraps를 :

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

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

나는 이것이 가장 깨끗한 접근법이라고 생각하며 어떤 식 으로든 ServiceForm에 영향을 미치지 않습니다 (즉, 서브 클래스 화를 어렵게 함).


답변

공식 문서 방법

장고 2.0 :

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

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


답변

함수에서 폼 클래스를 동적으로 작성하여 클로저를 통해 가맹점에 액세스 할 수 있습니다.

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 ~ mmarshall 답변) 클로저 솔루션을 좋아하지만 Django 폼에는 폼 세트에서 쿼리 세트를 필터링하는 데 사용할 수있는 콜백 메커니즘이 있습니다.

또한 장고 개발자가 좋아하지 않을만한 지표라고 생각합니다.

따라서 기본적으로 폼셋을 동일하게 만들지 만 콜백을 추가하십시오.

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

이것은 일반적인 아이디어를 제공해야합니다. 콜백을 이와 같은 객체 메소드로 만드는 것은 조금 더 복잡하지만 간단한 함수 콜백을 수행하는 것과는 달리 약간의 유연성을 제공합니다.


답변

나는 이것을 Carl Meyers 답변에 대한 의견으로 배치하고 싶었지만 포인트가 필요하기 때문에 여기에 배치했습니다. 알아내는 데 2 ​​시간이 걸렸으므로 누군가에게 도움이되기를 바랍니다.

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

나는 그 솔루션을 내 자신으로 사용했으며 inlineformset_factory로 시도 할 때까지 완벽하게 작동했습니다. Django 1.0.2를 실행 중이며 이상한 KeyError 예외가 발생했습니다. 최신 트렁크로 업그레이드했으며 직접 작동했습니다.

이제 다음과 비슷한 것을 사용할 수 있습니다.

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


답변

커밋 e091c18f50266097f648efc7cac2503968e9d217 8 월 14 일 23:44:46 2012 +0200에 접수 된 솔루션은 더 이상 작동하지 않습니다.

django.forms.models.modelform_factory () 함수의 현재 버전은 “유형 생성 기술”을 사용하여 전달 된 폼에서 type () 함수를 호출하여 메타 클래스 유형을 가져온 다음 결과를 사용하여 클래스 객체를 생성합니다. 즉석에서 입력 ::

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

이것은 양식 대신에 curryed 나 partial객체 라도 “오리가 당신을 물게 만든다”는 말을 의미합니다. ModelFormClass객체 의 구성 매개 변수를 가진 함수를 호출하고 다음과 같은 오류 메시지를 반환합니다.

function() argument 1 must be code, not str

이 문제를 해결하기 위해 클로저를 사용하여 첫 번째 매개 변수로 지정된 클래스의 하위 클래스를 반환하는 생성기 함수를 작성했습니다. 그런 다음 생성기 함수의 호출에 제공된 클래스로 kwargs 를 호출 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))

경고 :

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