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.partial 및 functools.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})
함수에서 폼 클래스를 동적으로 작성하여 클로저를 통해 가맹점에 액세스 할 수 있습니다.
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):
그것이 누군가를 도울 수 있기를 바랍니다.
I like the closure solution for being "cleaner" and more Pythonic (so +1 to mmarshall answer) but Django forms also have a callback mechanism you can use for filtering querysets in formsets.
It's also not documented, which I think is an indicator the Django devs might not like it as much.
So you basically create your formset the same but add the callback:
ServiceFormSet = forms.formsets.formset_factory(
ServiceForm, extra=3, formfield_callback=Callback('option', affiliate).cb)
This is creating an instance of a class that looks like this:
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
This should give you the general idea. It's a little more complex making the callback an object method like this, but gives you a little more flexibility as opposed to doing a simple function callback.
I wanted to place this as a comment to Carl Meyers answer, but since that requires points I just placed it here. This took me 2 hours to figure out so I hope it will help someone.
A note about using the inlineformset_factory.
I used that solution my self and it worked perfect, until I tried it with the inlineformset_factory. I was running Django 1.0.2 and got some strange KeyError exception. I upgraded to latest trunk and it worked direct.
I can now use it similar to this:
BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
BookFormSet.form = staticmethod(curry(BookForm, user=request.user))
As of commit e091c18f50266097f648efc7cac2503968e9d217 on Tue Aug 14 23:44:46 2012 +0200 the accepted solution can't work anymore.
The current version of django.forms.models.modelform_factory() function uses a "type construction technique", calling the type() function on the passed form to get the metaclass type, then using the result to construct a class-object of its type on the fly::
# Instatiate type(form) in order to use the same metaclass as form.
return type(form)(class_name, (form,), form_class_attrs)
This means even a curry
ed or partial
object passed instead of a form "causes the duck to bite you" so to speak: it'll call a function with the construction parameters of a ModelFormClass
object, returning the error message::
function() argument 1 must be code, not str
To work around this I wrote a generator function that uses a closure to return a subclass of any class specified as first parameter, that then calls super.__init__
after update
ing the kwargs with the ones supplied on the generator function's call::
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
Then in your code you'll call the form factory as::
MyFormSet = inlineformset_factory(ParentModel, Model,form = class_gen_with_kwarg(MyForm, user=self.request.user))
caveats:
- this received very little testing, at least for now
- supplied parameters could clash and overwrite those used by whatever code will use the object returned by the constructor
Carl Meyer's solution looks very elegant. I tried implementing it for modelformsets. I was under the impression that I could not call staticmethods within a class, but the following inexplicably works:
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))
In my view, if I do something like this:
formset = MyFormset(request.POST,queryset=MyModel.objects.all(),request=request)
Then the "request" keyword gets propagated to all of the member forms of my formset. I'm pleased, but I have no idea why this is working - it seems wrong. Any suggestions?
I spent some time trying to figure out this problem before I saw this posting.
The solution I came up with was the closure solution (and it is a solution I've used before with Django model forms).
I tried the curry() method as described above, but I just couldn't get it to work with Django 1.0 so in the end I reverted to the closure method.
The closure method is very neat and the only slight oddness is that the class definition is nested inside the view or another function. I think the fact that this looks odd to me is a hangup from my previous programming experience and I think someone with a background in more dynamic languages wouldn't bat an eyelid!
I had to do a similar thing. This is similar to the curry
solution:
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), ... )
I'm a newbie here so I can't add comment. I hope this code will work too:
ServiceFormSet = formset_factory(ServiceForm, extra=3)
ServiceFormSet.formset = staticmethod(curry(ServiceForm, affiliate=request.affiliate))
as for adding additional parameters to the formset's BaseFormSet
instead of form.
based on this answer I found more clear solution:
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
And run it in view like
formset_factory(form=ServiceForm.make_service_form(affiliate))
참고URL : https://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset
'development' 카테고리의 다른 글
C ++에서의 __FILE__, __LINE__ 및 __FUNCTION__ 사용법 (0) | 2020.06.16 |
---|---|
SQLAlchemy는 Django의 get_or_create에 해당합니까? (0) | 2020.06.16 |
서버에서 데이터를 얻는 권장 방법 (0) | 2020.06.16 |
ReactJS를 사용하여 입력 필드의 값을 얻는 방법은 무엇입니까? (0) | 2020.06.16 |
SQL Server에서 Cascading을 언제 사용해야하는 이유는 무엇입니까? (0) | 2020.06.16 |