Django पासिंग कस्टम फॉर्म पैरामीटर्स फॉर्मेट करने के लिए


150

यह Django 1.9 में form_kwargs के साथ तय किया गया था ।

मेरे पास एक Django फॉर्म है जो इस तरह दिखता है:

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 इस प्रकार व्यक्तिगत रूपों में?

जवाबों:


105

मैं का प्रयोग करेंगे 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 को प्रभावित नहीं करता है (यानी इसे उपवर्ग में कठिन बनाकर)।


यह मेरे लिए काम नहीं कर रहा है। मुझे त्रुटि मिलती है: विशेषता: '_curriedFormSet' ऑब्जेक्ट में कोई विशेषता नहीं है 'प्राप्त करें'
पाओलो बरगेंटिनो

मैं इस त्रुटि की नकल नहीं कर सकता। यह भी एक अजीब है क्योंकि एक फॉर्मेट में आमतौर पर 'गेट' विशेषता नहीं होती है, इसलिए ऐसा लगता है कि आप अपने कोड में कुछ अजीब कर रहे हैं। (इसके अलावा, मैंने '_curriedFormSet' जैसी विषमताओं से छुटकारा पाने के तरीके के साथ उत्तर को अपडेट किया)।
कार्ल मेयर

मैं इस पर दोबारा गौर कर रहा हूं क्योंकि मैं आपके समाधान के लिए काम करना चाहता हूं। मैं फॉर्मेट को ठीक घोषित कर सकता हूं, लेकिन अगर मैं इसे {{फॉरमेट}} कर प्रिंट करने की कोशिश करता हूं, तो जब मुझे "कोई विशेषता नहीं मिलती है" त्रुटि मिलती है। यह आपके द्वारा प्रदान किए गए समाधान के साथ होता है। यदि मैं फॉर्मसेट के माध्यम से लूप करता हूं और फॉर्मों को {{फॉर्म}} के रूप में प्रिंट करता हूं, तो मुझे फिर से त्रुटि मिलती है। उदाहरण के लिए यदि मैं लूप और {{form.as_table}} प्रिंट करता हूं, तो मुझे खाली फॉर्म टेबल मिलता है, यानी। कोई भी फ़ील्ड मुद्रित नहीं है। कोई विचार?
पाओलो बरगिनिनो

आप सही हैं, मुझे क्षमा करें; मेरा पहले का परीक्षण बहुत दूर नहीं गया था। मैंने इसे नीचे ट्रैक किया, और यह फॉर्मेट्स के आंतरिक रूप से काम करने के तरीके में कुछ विषमताओं के कारण टूट गया। समस्या के इर्द-गिर्द काम करने का एक तरीका है, लेकिन यह मूल लालित्य खोना शुरू कर देता है ...
कार्ल मेयर

5
अगर यहाँ टिप्पणी धागा समझ में नहीं आता है, यह है क्योंकि मैं सिर्फ functools.partialDjango के बजाय पायथन का उपयोग करने के लिए जवाब संपादित किया है django.utils.functional.curry। वे एक ही काम करते हैं, सिवाय इसके कि functools.partialनियमित पायथन फ़ंक्शन के बजाय एक अलग कॉल करने योग्य प्रकार देता है, और partialप्रकार एक उदाहरण विधि के रूप में बाँध नहीं करता है, जो समस्या को बड़े करीने से हल करता है यह टिप्पणी धागा मोटे तौर पर डीबगिंग के लिए समर्पित था।
कार्ल मेयर

81

आधिकारिक दस्तावेज़ मार्ग

Django 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


8
इसे अभी करने का सही तरीका होना चाहिए। स्वीकृत जवाब काम करता है और अच्छा है, लेकिन एक हैक है
Junchao Gu

निश्चित रूप से सबसे अच्छा जवाब और इसे करने का सही तरीका।
यानि १४


46

मैं एक फ़ंक्शन में गतिशील रूप से फॉर्म क्लास का निर्माण करूंगा, ताकि इसे संबद्धता तक पहुंच के माध्यम से बंद किया जा सके:

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

साभार - कि काम किया मैं इसे स्वीकार किए जाने के रूप में चिह्नित कर रहा हूं क्योंकि मैं उम्मीद कर रहा हूं कि एक क्लीनर विकल्प है, क्योंकि इस तरह से यह निश्चित रूप से कायरता महसूस करता है।
पाओलो बरगेंटिनो

स्पष्ट रूप से स्वीकार किए जाने के बाद से यह इसे करने का सबसे अच्छा तरीका है। अजीब लगता है, लेकिन चालबाजी करता है। :) धन्यवाद।
पाओलो बेरगीनो

कार्ल मेयर ने कहा, मुझे लगता है कि जिस तरह से आप तलाश कर रहे थे।
जारेट हार्डी

मैं Django ModelForms के साथ इस विधि का उपयोग कर रहा हूं।
chefsmart

मुझे यह समाधान पसंद है, लेकिन मुझे यकीन नहीं है कि इसे फॉर्मेट जैसे दृश्य में कैसे उपयोग किया जाए। क्या आपके पास कोई अच्छा उदाहरण है कि किसी दृश्य में इसका उपयोग कैसे करें? किसी भी सुझाव की सराहना की है।
जो जे

16

यह मेरे लिए काम किया है, Django 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
क्या आप मुझे समझा सकते हैं कि staticmethodयहाँ क्यों आवश्यक है?
12

9

मुझे "क्लीनर" और अधिक पायथोनिक (इसलिए mmarshall उत्तर के लिए +1) होने के लिए क्लोजर समाधान पसंद है, लेकिन Django रूपों में एक कॉलबैक तंत्र भी है जिसका उपयोग आप क्वेरी को फॉर्मसेट में फ़िल्टर करने के लिए कर सकते हैं।

यह भी प्रलेखित नहीं है, जो मुझे लगता है कि एक संकेतक है जो 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

यह आपको सामान्य विचार देना चाहिए। यह कॉलबैक को इस तरह एक वस्तु विधि बनाने के लिए थोड़ा अधिक जटिल है, लेकिन आपको एक साधारण फ़ंक्शन कॉलबैक करने के विपरीत थोड़ा अधिक लचीलापन देता है।


1
जवाब के लिए धन्यवाद। मैं अभी mmarshall के समाधान का उपयोग कर रहा हूं और चूंकि आप सहमत हैं कि यह अधिक पायथन है (कुछ मुझे नहीं पता होगा क्योंकि यह मेरी पहली पायथन परियोजना है) मुझे लगता है कि मैं इसके साथ चिपका हुआ हूं। हालांकि कॉलबैक के बारे में जानना निश्चित रूप से अच्छा है। एक बार फिर धन्यवाद।
पाओलो बेरगीनो

1
धन्यवाद। यह तरीका modelformset_factory के साथ बहुत अच्छा काम करता है। मुझे मॉडलफॉर्म के साथ काम करने के अन्य तरीके ठीक से नहीं मिल पाए लेकिन यह रास्ता बहुत सीधा था।
स्पाइक

करी कार्यात्मक अनिवार्य रूप से एक बंद बनाता है, है ना? आप ऐसा क्यों कहते हैं कि @ mmarshall का समाधान अधिक पायथोनिक है? Btw, आपके उत्तर के लिए धन्यवाद। मुझे यह तरीका पसंद है।
जोश

9

मैं कार्ल मेयर्स के जवाब के लिए इसे एक टिप्पणी के रूप में रखना चाहता था, लेकिन चूंकि मुझे उन बिंदुओं की आवश्यकता है जो मैंने इसे यहां रखा था। मुझे यह पता लगाने में 2 घंटे लगे इसलिए मुझे उम्मीद है कि यह किसी की मदद करेगा।

इनलाइनफॉर्मसेट_फैक्टिंग का उपयोग करने के बारे में एक नोट।

मैंने उस समाधान का उपयोग स्वयं किया और जब तक मैंने इनलाइनफॉर्मसेट_फैक्टिंग के साथ प्रयास नहीं किया, तब तक यह सही था। मैं Django 1.0.2 चला रहा था और कुछ अजीब KeyError अपवाद मिला। मैं नवीनतम ट्रंक में उन्नत हुआ और इसने प्रत्यक्ष काम किया।

मैं अब इसे इस तरह उपयोग कर सकते हैं:

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

एक ही बात के लिए चला जाता है modelformset_factory। इस उत्तर के लिए धन्यवाद!
thnee

9

Tue 14 अगस्त 23:44:46 2012 +0200 पर प्रतिबद्ध e091c18f50266097f648efc7cac2503968e9d217 के रूप में स्वीकृत समाधान अब और काम नहीं कर सकता।

Django.forms.models.modelform_factory () फ़ंक्शन का वर्तमान संस्करण एक "प्रकार निर्माण तकनीक" का उपयोग करता है, मेटाक्लस प्रकार प्राप्त करने के लिए उत्तीर्ण फॉर्म पर टाइप () फ़ंक्शन को कॉल करता है, फिर परिणाम का उपयोग करके वर्ग-ऑब्जेक्ट का निर्माण करता है। मक्खी पर टाइप करें ::

# 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

इसके चारों ओर काम करने के लिए मैंने एक जनरेटर फ़ंक्शन लिखा है जो किसी भी वर्ग के उप-वर्ग को पहले पैरामीटर के रूप में वापस करने के लिए एक क्लोजर का उपयोग करता है, जो तब जनरेटर फ़ंक्शन के कॉल पर आपूर्ति किए गए के साथ 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))

चेतावनियां:

  • यह बहुत कम परीक्षण प्राप्त हुआ, कम से कम अभी के लिए
  • आपूर्ति किए गए पैरामीटर क्लैश कर सकते हैं और जो कुछ भी कोड द्वारा उपयोग किया जाता है उसे कंस्ट्रक्टर द्वारा लौटाए गए ऑब्जेक्ट का उपयोग करेंगे

धन्यवाद, कुछ अन्य समाधानों के विपरीत, Django 1.10.1 में बहुत अच्छा काम करता है।
fpghost

1
@fpghost ध्यान रखें कि, कम से कम 1.9 तक (मैं कई कारणों से अभी भी 1.10 पर नहीं हूँ) यदि आपको बस इतना करना है कि क्वेरी को बदलना है, जिस पर फार्म का निर्माण किया गया है, तो आप इसे अपडेट कर सकते हैं। उपयोग करने से पहले इसकी .qusetset विशेषता को बदलकर MyFormSet लौटा दिया। इस विधि की तुलना में कम लचीला है, लेकिन पढ़ने / समझने के लिए बहुत सरल है।
RobM

3

कार्ल मेयर का समाधान बहुत ही सुरुचिपूर्ण दिखता है। मैंने इसे मॉडलफ़ॉर्मसेट के लिए लागू करने का प्रयास किया। मैं इस धारणा के तहत था कि मैं किसी वर्ग के भीतर स्टेथमैथोड्स को नहीं बुला सकता, लेकिन निम्नलिखित बेवजह काम करता है:

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)

फिर "अनुरोध" कीवर्ड मेरे सभी फॉर्मेट के सदस्य रूपों में प्रचारित हो जाता है। मैं प्रसन्न हूं, लेकिन मुझे नहीं पता कि यह क्यों काम कर रहा है - यह गलत लगता है। कोई सुझाव?


हम्म् ... अब अगर मैं MyFormSet के एक उदाहरण के फॉर्म विशेषता तक पहुँचने का प्रयास करता हूँ तो यह (सही ढंग से) <MyForm> के बजाय <फ़ंक्शन _curried> देता है। हालांकि वास्तविक रूप तक पहुंचने के बारे में कोई सुझाव, हालांकि? मैंने कोशिश की है MyFormSet.form.Meta.model
Trubliphone

वूप्स ... मुझे फॉर्म को एक्सेस करने के लिए क्यूरेड फंक्शन को कॉल करना होगा। MyFormSet.form().Meta.model। वास्तव में स्पष्ट है।
ट्रिब्लीफोन

मैं आपकी समस्या का समाधान करने की कोशिश कर रहा हूं, लेकिन मुझे लगता है कि मैं आपके पूरे उत्तर को पूरी तरह से नहीं समझता। किसी भी विचार अगर आपके दृष्टिकोण को मेरे मुद्दे पर यहां लागू किया जा सकता है? stackoverflow.com/questions/14176265/…
फाइनस्पिन

1

इस पोस्ट को देखने से पहले मैंने कुछ समय इस समस्या का पता लगाने में बिताया।

मैं जिस समाधान के साथ आया था वह क्लोजर समाधान था (और यह एक ऐसा समाधान है जिसका उपयोग मैंने पहले Django मॉडल फॉर्म के साथ किया था)।

मैंने ऊपर वर्णित करी () विधि की कोशिश की, लेकिन मैं अभी इसे Django 1.0 के साथ काम करने के लिए नहीं मिला, इसलिए अंत में मैं क्लोजर विधि पर वापस लौट आया।

बंद करने की विधि बहुत साफ है और केवल थोड़ी सी विषमता यह है कि वर्ग की परिभाषा दृश्य या किसी अन्य फ़ंक्शन के अंदर निहित है। मुझे लगता है कि तथ्य यह है कि यह मेरे लिए अजीब लगता है मेरे पिछले प्रोग्रामिंग अनुभव से एक हैंगअप है और मुझे लगता है कि अधिक गतिशील भाषाओं में पृष्ठभूमि वाला कोई व्यक्ति पलक नहीं झपकाएगा!


1

मुझे एक समान काम करना था। यह 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), ... )

1

इस उत्तर के आधार पर मुझे अधिक स्पष्ट समाधान मिला:

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

6
Django 1.9 ने इस अनावश्यक को बनाया, इसके बजाय form_kwargs का उपयोग करें।
पाओलो बेरेनटिनो

मेरे वर्तमान कार्य में हमें विरासत का उपयोग करने की आवश्यकता है django 1.7 ((
alexey_efimov

0

मैं यहाँ एक नौसिखिया हूँ इसलिए मैं टिप्पणी नहीं जोड़ सकता। मुझे उम्मीद है कि यह कोड भी काम करेगा:

ServiceFormSet = formset_factory(ServiceForm, extra=3)

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

फॉर्मेट के BaseFormSetबजाय फॉर्मेट के अतिरिक्त मापदंडों को जोड़ने के लिए ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.