2013-05-24 13 views
6

私は次のモデルを持っている:私はItemCollectionに追加することができるかどうかを制御したいDjangoの多対多関係の挿入制御

  1. class Item(models.Model): 
        # fields 
        # ... 
    
    class Collection(models.Model): 
        items = models.ManyToManyField(Item, related_name="collections") 
        # other fields 
        # ... 
    

    は今、私は二つのことをしたいです。

  2. Itemが追加または削除された場合は、Collectionにフィールドの一部を更新します。

2番目の問題については、関係の変更にフックするために使用できるdjango.db.models.signals.m2m_changedがあることを知っています。シグナルのコールバック内でCollectionを変更することはできますか?問題の挿入を「中断」するためにも信号を使用できますか?

+0

問題1の場合、フォームのクリーニングサイクルを使用してデータを検証する必要があります(検証メッセージングが容易になる)ので、[save_m2m](https://docs.djangoproject)に送信してください。 –

+1

@Hedde:私のデータはフォームから変更されない可能性が高いので、モデルに近いソリューションが望ましいです。 (CLIツールや公開されているAPIを通じて) – Constantinius

+0

少なくともロジックの一部についてはモデル保存メソッドを上書きすることができますが、APIを実装する場合は、そのロジックがAPIの承認レイヤーに属しているようです。 TastypieはDjangoでうまくいく素晴らしいAPIです。 –

答えて

8

私はあなたの希望の行動の両方にアプローチする最良の方法は、信号ではなく、むしろ(セーブでオーバーライドされたと思う)と引数を使用して明示的に定義するthroughテーブルの上に()メソッドを削除throughhttps://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.throughを参照してください。この:このようなhttps://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods

何か:

# -*- coding: utf-8 -*- 

from django.db import models 


class Item(models.Model): 
    # fields 
    # ... 

class Collection(models.Model): 
    items = models.ManyToManyField(Item, related_name="collections", through="CollectionItem") 
    # other fields 
    # ... 

class CollectionItem(models.Model): 
    collection = models.ForeignKey(Collection) 
    item = models.ForeignKey(Item) 

    def save(self, *args, **kwargs): 
     # Only allow this relationship to be created on some_condition 
     # Part 1 of your question. 
     if some_condition: 
      super(CollectionItem, self).save(*args, **kwargs) 

      # Update some fields on Collection when this 
      # relationship is created 
      # Part 2 of your question (1/2) 
      self.Collection.updateSomeFields() 

    def delete(self, *args, **kwargs): 
     collection = self.collection 
     super(CollectionItem, self).delete(*args, **kwargs) 

     # Update some fields on Collection when this relationship 
     # is destroyed. 
     # Part 2 of your question (2/2) 
     collection.updateSomeFields() 

ところで、あなたは関係を追加することがモデルを通じて、この節約信号を発生しますことがわかります。

シグナルについては、スルーテーブルを作成すると、pre_saveやpost_save信号を聞くことができますが、どちらも直接関係の作成を拒否することはできません。

モデルの1つまたは両方がサードパーティから提供されており、本当にスルーテーブルを作成できない場合は、信号経路が唯一の方法です。 、あなたがm2m_changedイベントを監視し、あなたのコレクションオブジェクト(あなたの質問の一部2)への更新をトリガし、遡及的に不適切に作成された関係(あなたの質問の一部1)を削除することができ、その場合には

https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed

。しかし、この後者のビットは醜いクルージングなので、私はあなたができる場合は、明示的なテーブルをスティックしたいと思います。

+1

私はこのアプローチがとても好きです。私はこれを扱う場所として 'trhough'モデルを使うことは考えていませんでしたが、意味があります。私が見る唯一の欠点は、 'ManyToManyField'の' add'メソッドを使うことができなくて、 'CollectionItem'自体を作成する必要があることです。これは何の問題もなく、慣れているだけのものです。ご回答有難うございます。 – Constantinius

+1

これは良い選択肢でもあり、より柔軟なものを提供します – Alp

+1

これは、admin [https://code.djangoproject.com/ticket/16073]で行われた変更に対してm2m_changedシグナルがどのように発生しないかを見ると、 – trubliphone

3
  1. pre_saveシグナルはインスタンスを保存する前に呼び出されます。しかし、そこからセーブ操作を中止することはできません。

    class Collection(models.Model): 
        items = models.ManyToManyField(Item, related_name="collections") 
    
        ... 
    
        def add_item(self, item): 
         if check_if_item_can_be_added(item): 
          items.add(item) 
          self.save() 
    
    def check_if_item_can_be_added(self, item): 
        # do your checks here 
    
  2. M2M分野にインスタンスを追加する場合は、Saveメソッドを取得していない:より良い解決策はItemを追加することができるかどうかをチェックする責任があるあなたのCollectionモデルに新しいメソッドを追加することですと呼ばれる。あなたは正しいです、m2m_changed信号が行く方法です。そこのコレクションインスタンスを安全に更新できます。