45

私はバンドの出席エントリーフォームに取り組んでいます。私のアイデアは、パフォーマンスやリハーサルのイベント情報を入力するフォームのセクションを用意することです。イベント テーブルのモデルは次のとおりです。

class Event(models.Model):
    event_id = models.AutoField(primary_key=True)
    date = models.DateField()
    event_type = models.ForeignKey(EventType)
    description = models.TextField()

次に、バンド メンバーをイベントにリンクし、出席、欠席、または免除のいずれであったかを記録するインライン FormSet が必要です。

class Attendance(models.Model):
    attendance_id = models.AutoField(primary_key=True)
    event_id = models.ForeignKey(Event)
    member_id = models.ForeignKey(Member)
    attendance_type = models.ForeignKey(AttendanceType)
    comment = models.TextField(blank=True)

ここで、私がやりたいことは、このインライン FormSet に現在のすべてのメンバーのエントリを事前に入力し、デフォルトで存在するようにすることです (約 60 メンバー)。残念ながら、この場合、Djangoは初期値を許可しません。

助言がありますか?

4

10 に答える 10

35

ですから、あなたはその答えを気に入らないでしょう。理由の 1 つは、まだコードを書き終えていないことと、多くの作業が必要だからです。

私がこれに遭遇したときに発見したように、あなたがする必要があるのは次のとおりです。

  1. フォームセットとモデル-フォームセットのコードをよく読んで、すべてがどのように機能するかを理解してください (一部の機能はフォームセット クラスに存在し、一部の機能はファクトリ関数に存在し、それらをアウト)。この知識は、後の手順で必要になります。
  2. からサブクラス化しBaseInlineFormSet、受け入れる独自のフォームセット クラスを記述しますinitial。ここで本当に注意が必要なのは、をオーバーライドする必要があり、直接の親または祖父母を使用するのではなく、 を呼び出すようにする必要があることです(これらはそれぞれとであり、どちらも初期データを処理できないため)。__init__()BaseFormSet.__init__()__init__()BaseInlineFormSetBaseModelFormSet
  3. 適切な管理インライン クラス (私の場合は ) の独自のサブクラスを作成し、そのメソッドをTabularInlineオーバーライドして、カスタム フォームセット クラスを使用した結果を返します。get_formsetinlineformset_factory()
  4. モデルの実際のModelAdminサブクラスでは、インライン、オーバーライド、add_viewおよびchange_viewほとんどのコードを複製しますが、大きな変更が 1 つあります。フォームセットに必要な初期データを作成し、それをカスタム フォームセットに渡します (これは、ModelAdminget_formsets()方法)。

Brian と Joseph とは、将来の Django リリースでこれを改善することについて、生産的な会話をいくつかしました。現時点では、モデル フォームセットの動作が通常よりも問題を引き起こしていますが、API を少し整理すれば、非常に簡単にできると思います。

于 2009-01-14T06:08:25.503 に答える
20

私はかなりの時間を費やして、サイト間で再利用できるソリューションを考え出しました。BaseInlineFormSetジェームズの投稿には、に対する呼び出しを拡張するが戦略的に呼び出すという知恵の重要な部分が含まれていましたBaseFormSet

AdminInline以下の解決策は、aとaの2つの部分に分かれていBaseInlineFormSetます。

  1. InlineAdmin公開されたリクエストオブジェクトに基づいて、動的に初期値を生成します。
  2. カリー化を使用BaseInlineFormSetして、コンストラクターに渡されるキーワード引数を介して初期値をカスタムに公開します。
  3. BaseInlineFormSetコンストラクターは、キーワード引数のリストから初期値をポップし、通常どおりに構築します。
  4. 最後の部分は、フォームの最大総数を変更し、BaseFormSet._construct_formandBaseFormSet._construct_formsメソッドを使用して、フォーム構築プロセスをオーバーライドしています。

OPのクラスを使用した具体的なスニペットを次に示します。これをDjango1.2.3に対してテストしました。開発中は、フォームセット管理ドキュメントを手元に置いておくことを強くお勧めします。

admin.py

from django.utils.functional import curry
from django.contrib import admin
from example_app.forms import *
from example_app.models import *

class AttendanceInline(admin.TabularInline):
    model           = Attendance
    formset         = AttendanceFormSet
    extra           = 5

    def get_formset(self, request, obj=None, **kwargs):
        """
        Pre-populating formset using GET params
        """
        initial = []
        if request.method == "GET":
            #
            # Populate initial based on request
            #
            initial.append({
                'foo': 'bar',
            })
        formset = super(AttendanceInline, self).get_formset(request, obj, **kwargs)
        formset.__init__ = curry(formset.__init__, initial=initial)
        return formset

forms.py

from django.forms import formsets
from django.forms.models import BaseInlineFormSet

class BaseAttendanceFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        """
        Grabs the curried initial values and stores them into a 'private'
        variable. Note: the use of self.__initial is important, using
        self.initial or self._initial will be erased by a parent class
        """
        self.__initial = kwargs.pop('initial', [])
        super(BaseAttendanceFormSet, self).__init__(*args, **kwargs)

    def total_form_count(self):
        return len(self.__initial) + self.extra

    def _construct_forms(self):
        return formsets.BaseFormSet._construct_forms(self)

    def _construct_form(self, i, **kwargs):
        if self.__initial:
            try:
                kwargs['initial'] = self.__initial[i]
            except IndexError:
                pass
        return formsets.BaseFormSet._construct_form(self, i, **kwargs)

AttendanceFormSet = formsets.formset_factory(AttendanceForm, formset=BaseAttendanceFormSet)
于 2010-09-22T04:35:19.163 に答える
3

django 1.7 を使用して、(渡されるモデルのインスタンスだけでなく) モデルに焼き付けられた追加のコンテキストを持つインライン フォームを作成する際に、いくつかの問題に遭遇しました。

フォーム セットに渡される ModelForm にデータを挿入するための別のソリューションを考え出しました。Python ではクラスを動的に作成できるため、フォームのコンストラクターを介してデータを直接渡すのではなく、渡したいパラメーターを使用してメソッドによってクラスを構築できます。その後、クラスがインスタンス化されると、メソッドのパラメーター。

def build_my_model_form(extra_data):
    return class MyModelForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(MyModelForm, self).__init__(args, kwargs)
            # perform any setup requiring extra_data here

        class Meta:
            model = MyModel
            # define widgets here

次に、インライン フォームセット ファクトリの呼び出しは次のようになります。

inlineformset_factory(ParentModel, 
                      MyModel, 
                      form=build_my_model_form(extra_data))
于 2015-02-07T19:55:54.670 に答える
2

フォームセットで empty_form ゲッターをオーバーライドできます。これは、django admin と組み合わせてこれを処理する方法の例です。

class MyFormSet(forms.models.BaseInlineFormSet):
    model = MyModel

    @property
    def empty_form(self):
        initial = {}
        if self.parent_obj:
            initial['name'] = self.parent_obj.default_child_name
        form = self.form(
            auto_id=self.auto_id,
            prefix=self.add_prefix('__prefix__'),
            empty_permitted=True, initial=initial
        )
        self.add_fields(form, None)
        return form    

class MyModelInline(admin.StackedInline):
    model = MyModel
    formset = MyFormSet

    def get_formset(self, request, obj=None, **kwargs):    
        formset = super(HostsSpaceInline, self).get_formset(request, obj, **kwargs)
        formset.parent_obj = obj
        return formset
于 2016-04-28T12:38:53.157 に答える
0

これが私が問題を解決した方法です。レコードの作成と削除には多少のトレードオフがありますが、コードはきれいです...

def manage_event(request, event_id):
    """
    Add a boolean field 'record_saved' (default to False) to the Event model
    Edit an existing Event record or, if the record does not exist:
    - create and save a new Event record
    - create and save Attendance records for each Member
    Clean up any unsaved records each time you're using this view
    """
    # delete any "unsaved" Event records (cascading into Attendance records)
    Event.objects.filter(record_saved=False).delete()
    try:
        my_event = Event.objects.get(pk=int(event_id))
    except Event.DoesNotExist:
        # create a new Event record
        my_event = Event.objects.create()
        # create an Attendance object for each Member with the currect Event id
        for m in Members.objects.get.all():
            Attendance.objects.create(event_id=my_event.id, member_id=m.id)
    AttendanceFormSet = inlineformset_factory(Event, Attendance, 
                                        can_delete=False, 
                                        extra=0, 
                                        form=AttendanceForm)
    if request.method == "POST":
        form = EventForm(request.POST, request.FILES, instance=my_event)
        formset = AttendanceFormSet(request.POST, request.FILES, 
                                        instance=my_event)
        if formset.is_valid() and form.is_valid():
            # set record_saved to True before saving
            e = form.save(commit=False)
            e.record_saved=True
            e.save()
            formset.save()
            return HttpResponseRedirect('/')
    else:
        form = EventForm(instance=my_event)
        formset = OptieFormSet(instance=my_event)
    return render_to_response("edit_event.html", {
                            "form":form, 
                            "formset": formset,
                            }, 
                            context_instance=RequestContext(request))
于 2010-03-08T20:27:17.883 に答える