157

これは 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()

このように個々のフォームにaffiliate=request.affiliateを渡すにはどうすればよいですか?

4

12 に答える 12

107

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 にはまったく影響を与えません (つまり、サブクラス化が難しくなります)。

于 2009-03-08T18:00:26.520 に答える
103

公式文書の方法

ジャンゴ 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

于 2016-03-05T06:41:12.803 に答える
47

クロージャーを介してアフィリエイトにアクセスできるように、フォームクラスを関数で動的に構築します。

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)
    ...
于 2009-03-08T04:24:10.023 に答える
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):

それが誰かを助けることを願って、それを理解するのに十分な時間がかかりました;)

于 2014-09-18T14:29:59.323 に答える
10

私は「よりクリーン」でよりPythonicであるためのクロージャーソリューションが好きです(したがって、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

これはあなたに一般的な考えを与えるはずです。コールバックをこのようなオブジェクトメソッドにするのは少し複雑ですが、単純な関数コールバックを実行するのとは対照的に、もう少し柔軟性があります。

于 2009-03-08T07:08:41.720 に答える
10

これをカール・マイヤーズの回答へのコメントとして配置したかったのですが、それにはポイントが必要なので、ここに配置しました。これを理解するのに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))
于 2009-05-01T23:00:06.300 に答える
10

2012 年 8 月 14 日火曜日 23:44:46 +0200 のコミット e091c18f50266097f648efc7cac2503968e9d217 の時点で、受け入れられたソリューションはもう機能しません。

現在のバージョンの 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 を ing した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))

注意:

  • 少なくとも今のところ、これはほとんどテストされていません
  • 指定されたパラメーターは、コンストラクターによって返されたオブジェクトを使用するコードによって使用されるものと衝突して上書きされる可能性があります
于 2014-09-10T13:09:20.300 に答える
3

CarlMeyerのソリューションは非常にエレガントに見えます。モデルフォームセットに実装してみました。クラス内でstaticmethodsを呼び出すことができないという印象を受けましたが、次のことが不可解に機能します。

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」キーワードがフォームセットのすべてのメンバーフォームに伝達されます。嬉しいですが、なぜこれが機能しているのかわかりません。間違っているようです。助言がありますか?

于 2012-02-09T07:16:01.840 に答える
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), ... )
于 2010-10-27T12:12:55.947 に答える
1

この投稿を見る前に、この問題を理解しようと時間を費やしました。

私が思いついた解決策は、クロージャー ソリューションでした (これは、以前に Django モデル フォームで使用したソリューションです)。

上記のようにカレー()メソッドを試しましたが、Django 1.0で動作させることができなかったため、最終的にクロージャメソッドに戻りました。

クロージャー メソッドは非常によくできており、クラス定義がビューまたは別の関数内にネストされていることだけが少し奇妙です。これが私にとって奇妙に見えるという事実は、以前のプログラミング経験からのハングアップであり、より動的な言語のバックグラウンドを持つ人はまぶたを打つことはないと思います!

于 2009-08-27T14:38:38.377 に答える
0

この回答に基づいて、より明確な解決策を見つけました:

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))
于 2016-08-30T13:59:30.253 に答える