2009-03-25 18 views
7

私はモデル間で疎結合をモデル化するために私のモデルではnullになることができるForeignKeyを持っています。Nullable ForeignKeysと参照されたモデルインスタンスを削除する

class Message(models.Model): 
    sender = models.ForeignKey(User, null=True, blank=True) 
    sender_name = models.CharField(max_length=255) 

保存時に、送信者名がsender_name属性に書き込まれます。今、私は送信者によって参照されたUserインスタンスを削除し、そのメッセージをそのまま残したいと考えています。

ユーザーのインスタンスを削除するとすぐに、このコードでメッセージが削除されます。だから私はシグナルハンドラが良い考えだと思った。

def my_signal_handler(sender, instance, **kwargs): 
    instance.message_set.clear() 
pre_delete.connect(my_signal_handler, sender=User) 

悲しいことに、決して解決策ではありません。どうにかして、Djangoはまず削除したいものを集め、pre_deleteハンドラを起動します。

アイデア?私の脳の結び目はどこですか?

答えて

13

実際にDjangoはSQLのON DELETE CASCADEの動作をエミュレートしますが、これを変更する方法はドキュメントに記載されていません。彼らがここで言及する文書は、このセクションの最後にあります:Deleting objects

Djangoはすべての関連するモデルインスタンスを収集してから、それぞれに対して事前削除ハンドラを呼び出します。シグナルの送信者は削除しようとしているモデルクラスです。この場合はUserではなく、Messageとなります。これは、ユーザーによってトリガされたカスケード削除と通常の削除の違いを検出するのを困難にします...特にシグナル最後に削除されたのはUserクラスの削除です:-)

しかし、User.delete()関数を呼び出す前に、Djangoが削除を提案しているオブジェクトのリストを取得することはできます。各モデルインスタンスには_collect_sub_objects()という半個人的なメソッドがあり、それを指す外部キーを持つインスタンスのリストをコンパイルします(インスタンスを削除せずにこのリストをコンパイルします)。 delete()django.db.baseに見ると、このメソッドの呼び出し方法を見ることができます。

これが自分のオブジェクトの1つだった場合、_collect_sub_objects()を実行するためにインスタンス上でdelete()メソッドをオーバーライドしてから、スーパークラスの削除を呼び出す前にForeignKeysを中断することをお勧めします。サブクラス化が難しい組み込みのDjangoオブジェクトを使用しているので(独自のUserオブジェクトをdjangoに代入することは可能ですが)、ビューロジックを使用して_collect_sub_objectsを実行してからFKを破棄する必要があります削除。ここで

は間に合わせと-例です:

from django.db.models.query import CollectedObjects 
u = User.objects.get(id=1) 


instances_to_be_deleted = CollectedObjects() 
u._collect_sub_objects(instances_to_be_deleted) 

for k in instances_to_be_deleted.ordered_keys(): 
    inst_dict = instances_to_be_deleted.data[k] 
    for i in inst_dict.values(): 
     i.sender = None # You will need a more generic way for this 
     i.save() 

u.delete() 
+0

説明していただきありがとうございます。 –

+0

徹底的な答え。 –

0

だけでONがCASCADEの行動を自分でDELETEを発見したので、私は、Djangoの1.3で、彼らは外国キーの動作configurableを行っていることがわかります。

関連する問題