Djangoの QuerySet.delete()
を実行した時に実際に、発行されるクエリがちょっと想定と違ったので、メモ。Django1.7、データベースはMySQLで確認しています。
準備
- 以下のようなmodels.pyを用意。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author)
- テストデータを用意。
>>> from books.models import *
>>> a1 = Author.objects.create(name='Alice')
>>> a2 = Author.objects.create(name='Bob')
>>> Book.objects.create(title='book1', author=a1)
>>> Book.objects.create(title='book2', author=a1)
>>> Book.objects.create(title='comic1', author=a2)
>>> Book.objects.create(title='comic2', author=a2)
実験
QuerySet.delete()
をいろいろな条件で実行してみた。
- 単にfieldで絞り込む場合
>>> Book.objects.filter(title='comic1').delete()
DELETE FROM `books_book` WHERE `books_book`.`title` = 'comic1';
素直にDELETE FROM テーブル WHERE 条件
だった。
- 外部テーブルを参照する場合
>>> Book.objects.filter(author__name='Bob').delete()
SELECT `books_book`.`id` FROM `books_book` INNER JOIN `books_author` ON ( `books_book`.`author_id` = `books_author`.`id` ) WHERE `books_author`.`name` = 'Bob';
DELETE FROM `books_book` WHERE `books_book`.`id` IN (11, 10);
SELECT id FROM テーブル INNER JOIN ... WHERE 条件
してから
DELETE FROM テーブル WHERE id IN (...)
していた。
- 親テーブルをdeleteする場合
>>> Author.objects.all().delete()
SELECT `books_author`.`id`, `books_author`.`name` FROM `books_author`;
DELETE FROM `books_book` WHERE `books_book`.`author_id` IN (7, 8);
DELETE FROM `books_author` WHERE `books_author`.`id` IN (8, 7);
SELECT id FROM 親テーブル INNER JOIN ... WHERE 条件
してから、子テーブルの関連するレコードをDELETEして、その後親テーブルのレコードをDELETE。
ここ曰く、
Django は、オブジェクトを削除する際に、通常 SQLでいう ON DELETE CASCADE 制約をエミュレートします。すなわち、 削除対象のオブジェクトを指すような外部キーを持つ全てのオブジェクトも 同時に削除されるのです。
とのこと。リンク先の例ではインスタンスのdeleteメソッドだけど、QuerySetでも同じような動作になるようだ。
まとめ
外部テーブル参照したり、親テーブルの場合、想定通りの動作か確認しておいたほうがいい。
deleteに限らず、ORM経由で操作するときはどんなクエリになるか確認しておいたほうが良さげ。