2012-09-01 16 views
5

中間モデルを使用する2つのモデル間にM2M関係があります。議論のために、のは、マニュアルからの例を使用してみましょう:中間モデルとのM2M関係のクラスベースビュー

class Person(models.Model): 
    name = models.CharField(max_length=128) 

    def __unicode__(self): 
     return self.name 

class Group(models.Model): 
    name = models.CharField(max_length=128) 
    members = models.ManyToManyField(Person, through='Membership') 

    def __unicode__(self): 
     return self.name 

class Membership(models.Model): 
    person = models.ForeignKey(Person) 
    group = models.ForeignKey(Group) 
    date_joined = models.DateField() 
    invite_reason = models.CharField(max_length=64) 

私はCRUDハンドリング意見を書いて避けるために、Djangoのクラスベースのビューを使用するようにしたいと思います。しかし、私は、デフォルトのCREATEVIEWを使用しようとすると、それは動作しません:

class GroupCreate(CreateView): 
    model=Group 

これは、グループオブジェクト上のすべてのフィールドを持つフォームをレンダリングし、メンバーフィールドの複数選択ボックスを与え、これは単純なM2M関係に適しています。ただし、date_joinedまたはinvite_reasonを指定する方法はありません。フォームを送信すると、次のAttributeErrorが返されます。

"ManyToManyFieldに中間モデルを指定する値を設定できません。代わりにメンバーシップマネージャを使用してください。

一般的なCreateViewの一部をオーバーライドする方法や、独自のカスタムビューを作成してmixinで行う方法はありますか?管理インターフェースはインラインを使用して中間体とのM2M関係を自動的に処理するため、フレームワークの一部である必要があります。

+0

重複可能[django]中間モデルを指定するManyToManyFieldに値を設定できません。代わりにマネージャを使用する](http://stackoverflow.com/questions/3091328/django-cannot-set-values-on-a-manytomanyfield-which-specifies-an-intermediary-mo) – juliocesar

答えて

6

あなたはCreateViewを拡張する必要があります。

from django.views.generic import CreateView 

class GroupCreate(CreateView): 
    model=Group 

form_valid()を上書き:

from django.views.generic.edit import ModelFormMixin 
from django.views.generic import CreateView 

class GroupCreate(CreateView): 
    model = Group 

    def form_valid(self, form): 
     self.object = form.save(commit=False) 
     for person in form.cleaned_data['members']: 
      membership = Membership() 
      membership.group = self.object 
      membership.person = person 
      membership.save() 
     return super(ModelFormMixin, self).form_valid(form) 

documentationあなたはgroupperson間の各関係のための新しいmembership Sを作成する必要があり、言うように。

私はここに form_validオーバーライド見

Using class-based UpdateView on a m-t-m with an intermediary model

+0

私はこれをテストするチャンスを持っていませんでした(そして私は別のルートを下ってしまいました)が、正しい方法のように見えます。乾杯! – Symmetric

+0

これはあなたに 'date_joined'と' invite_reason'を入力する機会を与えません...それはそれらを空に保存します... –

0

私は数日前にかなり同じ問題に直面していました。 Djangoは中間のm2m関係を処理するのに問題があります。

これは私が有用であることが判明したもののソリューションです:

1. Define new CreateView 
class GroupCreateView(CreateView): 
    form_class = GroupCreateForm 
    model = Group 
    template_name = 'forms/group_add.html' 
    success_url = '/thanks' 

次に定義されたフォームの保存方法に変更 - GroupCreateFormを。保存はDBに永久的な変更を加える責任があります。私はORMを通じてこの作業を行うことができませんでしたので、私はあまりにも生のSQLを使用しました:

1. Define new CreateView 
class GroupCreateView(CreateView): 


class GroupCreateForm(ModelForm): 
    def save(self): 
     # get data from the form 
     data = self.cleaned_data 
     cursor = connection.cursor() 
     # use raw SQL to insert the object (in your case Group) 
     cursor.execute("""INSERT INTO group(group_id, name) 
          VALUES (%s, %s);""" (data['group_id'],data['name'],)) 
     #commit changes to DB 
     transaction.commit_unless_managed() 
     # create m2m relationships (using classical object approach) 
     new_group = get_object_or_404(Group, klient_id = data['group_id']) 
     #for each relationship create new object in m2m entity 
     for el in data['members']: 
      Membership.objects.create(group = new_group, membership = el) 
     # return an object Group, not boolean! 
     return new_group 

注:あなたが見ることができるように私は、モデルを少し変更しました(私は独自のを持っていますIntegerFieldとは、主キーのために、シリアルを使用していないことは、参考のために「それは

+0

この回答はあまり役に立ちません。あなたのモデルを見せていただけますか? pkを "id"から "group_id"に変更することはあまり意味がありません。 – Timo

0

get_object_or_404になった方法です、私はクラスベースのビューを使用して終了していない、代わりに私はこのようなものでした:。

def group_create(request): 
    group_form = GroupForm(request.POST or None) 
    if request.POST and group_form.is_valid(): 
     group = group_form.save(commit=False) 
     membership_formset = MembershipFormSet(request.POST, instance=group) 
     if membership_formset.is_valid(): 
      group.save() 
      membership_formset.save() 
      return redirect('success_page.html') 
    else: 
     # Instantiate formset with POST data if this was a POST with an invalid from, 
     # or with no bound data (use existing) if this is a GET request for the edit page. 
     membership_formset = MembershipFormSet(request.POST or None, instance=Group()) 

    return render_to_response(
     'group_create.html', 
     { 
      'group_form': recipe_form, 
      'membership_formset': membership_formset, 
     }, 
     context_instance=RequestContext(request), 
    ) 

をこれはクラスベースの実装の出発点になるかもしれませんが、それはwこれをクラスベースのパラダイムに振り回そうとしています。

0

ちょうど1コメント、CBVを使用しているときにして、フォームを保存する必要がTrue =コミット、そうするために使用することができ、そのグループが作成され、IDが指定されていますメンバーシップを作成します。 それ以外の場合は、commit = Falseの場合、グループオブジェクトにはまだIDがなく、エラーが発生しています。

+0

これについてもっと知りたいチャンス?受け入れられた答えに続いて、 'Membership'を保存するコードに辿ることのできる唯一の方法は、' commit = False'です。そうでなければ、質問に示されている 'AttributeError'を投げます。実際、 'Membership'を保存した後に' form.save() 'を使用しようとしても、私はまだそのAttributeErrorを取得します。しかし、あなたが言うように、 'group'の' id'はないので、 'Membership'は正しく保存されていません... –

+0

私が言ったのは、提案された解決策がうまくいかないということです。グループインスタンスはまだDBに保存されていないので、IDはありません。あなたがしなければならないのは、名前だけで新しいグループを作り、それを保存することです(コミット= True)。これにはIDがあり、新しいMembershipオブジェクトを作成できます。エラーについて、私は正確な理由を知らないので、私はコードを試していない。おそらく、自動生成されたフォームにはメンバーのある必須フィールドが含まれています...これについてさらに詳細はありますか? – kiril

+0

新しいIDで新しいグループを作成するのは良いようですが、Zad-Manが言ったように、date_joinedとinvite_reasonをどのように入力すればいいですか?ビュー、モデルを持つ完全なソリューションを誰かが持っています。私はそれが "admin"アプリがそれをどのように解決したかを見るのに役立つと思う。 – Timo

2
class GroupCreate(CreateView): 
    model = Group 

    def form_valid(self, form): 
     self.object = form.save(commit=False) 

     ### delete current mappings 
     Membership.objects.filter(group=self.object).delete() 

     ### find or create (find if using soft delete) 
     for member in form.cleaned_data['members']: 
      x, created = Membership.objects.get_or_create(group=self.object, person=member) 
      x.group = self.object 
      x.person = member 
      #x.alive = True # if using soft delete 
      x.save() 
     return super(ModelFormMixin, self).form_valid(form)