Djangoでデータを取得してみる(ManyToMany)
データの準備
モデルを作成。
$ vi books/models.py
・・・ class Store(models.Model): name = models.CharField(max_length=100) class Book(models.Model): title = models.CharField(max_length=100) price = models.IntegerField() author = models.ForeignKey(Author, on_delete=models.CASCADE) stores = models.ManyToManyField("Store")
マイグレーションを実施。
$ python3 manage.py makemigrations $ python3 manage.py migrate
ManyToManyを指定すると、自動的に中間テーブルが作られる。
$ sqlite3 db.sqlite3 > .schema books_book_stores CREATE TABLE IF NOT EXISTS "books_book_stores" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "book_id" bigint NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED, "store_id" bigint NOT NULL REFERENCES "books_store" ("id") DEFERRABLE INITIALLY DEFERRED ); CREATE UNIQUE INDEX "books_book_stores_book_id_store_id_e76b3c7a_uniq" ON "books_book_stores" ("book_id", "store_id"); CREATE INDEX "books_book_stores_book_id_43c79a35" ON "books_book_stores" ("book_id"); CREATE INDEX "books_book_stores_store_id_d8b84690" ON "books_book_stores" ("store_id");
データを作成し、投入。
$ vi books/fixtures/initial_data.json [ ・・・ {"model": "books.store", "pk": 1, "fields": {"name": "ショップA"}}, {"model": "books.store", "pk": 2, "fields": {"name": "ショップB"}}, {"model": "books.store", "pk": 3, "fields": {"name": "ショップC"}}, {"model": "books.book", "pk": 1, "fields": {"title": "book1", "price": 1000, "author_id":1, "stores":[1,2,3]}}, {"model": "books.book", "pk": 2, "fields": {"title": "book2", "price": 2000, "author_id":1, "stores":[]}}, {"model": "books.book", "pk": 3, "fields": {"title": "book3", "price": 3000, "author_id":1, "stores":[1]}}, {"model": "books.book", "pk": 4, "fields": {"title": "book4", "price": 4000, "author_id":2, "stores":[2,3]}}, {"model": "books.book", "pk": 5, "fields": {"title": "book5", "price": 5000, "author_id":2, "stores":[]}} ] $ python3 manage.py loaddata initial_data.json
ManyToManyのフィールドには、「"stores":[1,2,3]」のような感じで、配列で指定すると、 中間テーブルにデータが設定される。
> select * from books_book_stores;
id | book_id | store_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
4 | 3 | 1 |
5 | 4 | 2 |
6 | 4 | 3 |
データの取得
storeテーブルのデータは、「book.stores.all」で参照できる。
・・・ class BookList(ListView): model = Book ・・・
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> {% for book in object_list %} {{ book.title }}<br> {% for store in book.stores.all %} ・{{ store.name}}<br> {% endfor %} {% endfor %} </body> </html>
prefetch_related
上記のままだと、sotresを参照するたびにSQLが実行されるが、 「prefetch_related('stores')」を付けると、まとめてデータを取得するので、 SQLの実行回数が減る。
・・・ class BookList(ListView): model = Book queryset = Book.objects.all().prefetch_related('stores') ・・・
絞り込み
「stores__name」のような感じで条件を指定することも可。
・・・ class BookList(ListView): model = Book queryset = Book.objects.filter(stores__name='ショップA') ・・・
逆参照
Book側からは「sotres」で参照できたが、Stores側からは「book_set」で参照できる。