Django:フォームを使用した1つのテンプレート内の複数のモデル[終了]


113

サポートチケット追跡アプリを作成していて、1つのページから作成したいモデルがいくつかあります。チケットは、ForeignKeyを介して顧客に属します。ノートは、ForeignKeyを介してチケットにも属します。顧客(まったく別のプロジェクト)を選択するか、新しい顧客を作成してからチケットを作成し、最後に新しいチケットに割り当てるメモを作成するオプションが欲しいのですが。

私はDjangoにかなり慣れていないので、毎回新しい機能を試し、繰り返し作業する傾向があります。ModelFormsを使用したことがありますが、一部のフィールドを非表示にして、複雑な検証を行います。私が探しているコントロールのレベルにはフォームセットが必要か、手作業ですべてを行う必要があるようです。

私が見逃している素敵な機能はありますか?誰かがフォームセットを使用するための良いリファレンスや例を持っていますか?私はそれらのAPIドキュメントに週末全体を費やしましたが、私はまだ無知です。すべてを分解して手動でコーディングすると、設計上の問題になりますか?


最初に顧客フォームを検証し、それが有効である場合は、request.POST(new_data = request.POST.copy())からコピーを作成してから、顧客IDを(検証された顧客フォームから)取得し、new_dataを更新して、 customer idはforeignkeyフィールドの値(多分モデルのcustomer)です。最後に、2番目のフォーム(チケット)を検証するためにnew_dataを検討します
Negar37

回答:


87

これは、ModelFormsで実装するのはそれほど難しくありません。フォームA、B、Cがあるとしましょう。各フォームとページを印刷して、POSTを処理する必要があります。

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

ここではカスタム検証のためのドキュメントです。


2
ところで、フォームセットはあなたが説明した問題の良い解決策ではないと思います。モデルの複数のインスタンスを表すために常に使用しました。たとえば、申請者フォームがあり、3つの参照が必要な場合、参照モデルの3つのインスタンスを持つフォームセットを作成します。
ジェイソンクリスタ

1
この方法では、.is_valid()呼び出しが短絡されないことに注意してください。ショートさせたい場合は、「and」になるまで.is_valid()関数の呼び出しを遅らせる必要があります。
Lie Ryan

66

私はちょうど前日と同じ状況にあった、そしてここに私の2セントがある:

1)間違いなく、ここで単一の形式の複数のモデルエントリの最も短くて簡潔なデモンストレーションを見つけました:http : //collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/

一言で言えば、各モデルのフォームを作成し、両方を1つ<form>のでテンプレートに送信します。prefix keyargを、ビューハンドルを検証します。依存関係がある場合は、依存する前に必ず「親」モデルを保存し、「子」モデルの保存をコミットする前に外部キーに親のIDを使用してください。リンクにはデモがあります。

2)たぶんこれを行うにはフォームセットをたたくことができますが、私が掘り下げた限り、フォームセットは主に同じモデルの複数を入力するためのもので、オプションで外部キーによって別のモデルに結び付けることができます。ただし、複数のモデルのデータを入力するためのデフォルトのオプションはないようで、フォームセットが意図しているようには見えません。


26

私はごく最近、いくつかの問題を抱えており、これを行う方法を理解しました。プライマリ、B、Cの3つのクラスがあり、B、Cがプライマリへの外部キーを持っていると仮定します。

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

このメソッドを使用すると、必要な検証をすべて実行できるだけでなく、同じページに3つすべてのオブジェクトを生成できます。また、JavaScriptと非表示フィールドを使用して、同じページで複数のB、Cオブジェクトを生成できるようにしました。


3
この例では、モデルBおよびCの外部キーをプライマリモデルを指すように設定するにはどうすればよいですか。
ユーザー

同じフォームに表示したいモデルが2つだけあります。しかし、exclude =( 'primary'、)ステートメントを取得できません。プライマリとは何ですか?2つのモデルCustomerConfigとContractがある場合。契約には、CustomerConfigへの外部キーがあります。customer_config = models.ForeignKey( 'CustomerPartnerConfiguration')など 'primary'とは何ですか?
pitchblack408 14

10

からのMultiModelFormdjango-betterformsは、Gnudiffの回答で説明されていることを行うための便利なラッパーです。これModelFormは、通常のを単一のフォームとして透過的に(少なくとも基本的な使用法では)使用される単一のクラスにラップします。以下のドキュメントから例をコピーしました。

# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs

答えを見つける前にdjango-betterforms、そのMultiModelFormクラスを見ただけです。彼らの解決策は本当に良さそうですが、しばらく更新されていないようです。まだこの@jozxyqkを使用していますか?問題ありませんか?
強化

@enchance数年になります。当時、私はそれが便利であり、より優れた選択肢の1つであることに気付きました。あまり気に入らない場合は、時間を節約できます。簡単なフォームをカスタマイズして実行したいときは、独自のフォームをロールする方が簡単だと想像できます。ビューでフォームとコンテキストを簡単に組み合わせることが、私がdjangoで見逃した最初の機能です。
jozxyqk

返信男をありがとう。私はそれをフォークすることを考えています、そしておそらく途中でいくつかのことを更新します。これまで見てきたことから、問題なく機能しています。あなたの言う通り、時間を大幅に節約できます。
強化

5

現在、回避策の機能があります(単体テストに合格しています)。他のモデルから限られた数のフィールドのみを追加したい場合、これは私の意見に対する良い解決策です。

ここで何か不足していますか?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile

3

「一部のフィールドを非表示にし、複雑な検証を行いたい」

組み込みの管理インターフェースから始めます。

  1. ModelFormをビルドして、必要なフィールドを表示します。

  2. フォーム内の検証ルールを使用してフォームを拡張します。通常、これはcleanメソッドです。

    この部分が適切に機能することを確認してください。

これが完了すると、組み込みの管理インターフェースから離れることができます。

次に、1つのWebページで、部分的に関連する複数のフォームを操作します。これは、すべてのフォームを1つのページに表示するための一連のテンプレートです。

次に、ビュー関数を記述して、さまざまなフォームを読み取り、検証し、さまざまなオブジェクトsaves()を実行する必要があります。

「すべてを分解して手動でコーディングすると、設計上の問題になりますか?」いいえ、多くの利益を得るための多くの時間です。


方法がわからないので、やらないでください
orokusaki '23 / 09/23

1
@orokusaki:他に何が欲しいですか?それは解決策を説明しているようです。これ以上何を言うべきですか?質問は曖昧なので、実際のコードを提供することは困難です。文句を言うのではなく、改善のための提案を提供してください。何を指示してるんですか?
S.Lott

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.