Djangoでデータを取得してみる(外部キー)
データの準備
モデルを作成。
$ vi books/models.py
from django.db import models class Author(models.Model): GENDER = ( (1, '男'), (2, '女') ) name = models.CharField(max_length=100) gender = models.IntegerField(choices=GENDER) class Book(models.Model): title = models.CharField(max_length=100) price = models.IntegerField() author = models.ForeignKey(Author, on_delete=models.CASCADE)
一旦、DBをまっさらにして、
$ rm db.sqlite3
マイグレーションを実施。
$ python3 manage.py makemigrations $ python3 manage.py migrate
データを作成し、投入
$ vi books/fixtures/initial_data.json [ {"model": "books.author", "pk": 1, "fields": {"name": "太郎", "gender": 1}}, {"model": "books.author", "pk": 2, "fields": {"name": "花子", "gender": 2}}, {"model": "books.book", "pk": 1, "fields": {"title": "book1", "price": 1000, "author_id":1}}, {"model": "books.book", "pk": 2, "fields": {"title": "book2", "price": 2000, "author_id":1}}, {"model": "books.book", "pk": 3, "fields": {"title": "book3", "price": 3000, "author_id":1}}, {"model": "books.book", "pk": 4, "fields": {"title": "book4", "price": 4000, "author_id":2}}, {"model": "books.book", "pk": 5, "fields": {"title": "book5", "price": 5000, "author_id":2}} ] $ python3 manage.py loaddata initial_data.json
authorテーブル
id | name | gender |
---|---|---|
1 | 太郎 | 1 |
2 | 花子 | 2 |
bookテーブル
id | title | price | author_id |
---|---|---|---|
1 | book1 | 1000 | 1 |
2 | book2 | 2000 | 1 |
3 | book3 | 3000 | 1 |
4 | book4 | 4000 | 2 |
5 | book5 | 5000 | 2 |
データの取得
親テーブルのデータは、「book.author.name」みたいな感じで参照できる。
$ vi books/templates/books/book_list.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> {% for book in object_list %} {{ book.title }}({{ book.author.name}})<br> {% endfor %} </body> </html>
しかし、このままでは、参照する度にSQLが実行される
- SELECT … FROM "books_book"
- SELECT … FROM "books_author" WHERE "books_author"."id" = '1'
- SELECT … FROM "books_author" WHERE "books_author"."id" = '1'
- SELECT … FROM "books_author" WHERE "books_author"."id" = '1'
- SELECT … FROM "books_author" WHERE "books_author"."id" = '2'
- SELECT … FROM "books_author" WHERE "books_author"."id" = '2'
select_related
select_related()を付けると、
・・・ class BookList(ListView): model = Book queryset = Book.objects.all().select_related()
下記の1回のSQLの実行で済む。
SELECT "books_book"."id", "books_book"."title", "books_book"."price", "books_book"."author_id", "books_author"."id", "books_author"."name", "books_author"."gender" FROM "books_book" INNER JOIN "books_author" ON ("books_book"."author_id" = "books_author"."id")
親テーブルで絞り込み
「親のモデル名__カラム名」で絞り込むこともできる。
・・・ class BookList(ListView): model = Book queryset = Book.objects.filter(author__name='花子')
この場合、bookの検索時には、下記のSQLが実行される。
SELECT "books_book"."id", "books_book"."title", "books_book"."price", "books_book"."author_id" FROM "books_book" INNER JOIN "books_author" ON ("books_book"."author_id" = "books_author"."id") WHERE "books_author"."name" = '''花子'''
データの取得(Author側)
authorも一覧を表示できるよう、urls.pyとviews.pyを修正。
・・・ urlpatterns = [ path('', views.BookList.as_view(), name='book_list'), path('authors', views.AuthorList.as_view(), name='author_list'), ]
・・・ from .models import Author class BookList(ListView): model = Book class AuthorList(ListView): model = Author
「book_set.all」のように指定すると、子のテーブルのデータが取得できる。
※choicesの表示名は「get_カラム名_display」で取得できる。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> {% for author in object_list %} {{ author.name }}({{ author.get_gender_display }})<br> {% for book in author.book_set.all %} ・{{ book.title}}<br> {% endfor %} {% endfor %} </body> </html>
しかし、このままでは、参照する度にSQLが実行される。
- SELECT … FROM "books_author"
- SELECT … FROM "books_book" WHERE "books_book"."author_id" = '1'
- SELECT … FROM "books_book" WHERE "books_book"."author_id" = '2'
prefetch_related
prefetch_relatedを指定すると、
・・・ class AuthorList(ListView): model = Author queryset = Author.objects.all().prefetch_related("book_set")
2回のSQLで済むようになる。
- SELECT … FROM "books_author"
- SELECT … FROM "books_book" WHERE "books_book"."author_id" IN ('1', '2')
子テーブルで絞り込み
「子のモデル名__カラム名」で絞り込むこともできる。
・・・ class AuthorList(ListView): model = Author queryset = Author.objects.filter(book__title="book1")
この場合、bookの検索時には、下記のSQLが実行される。
SELECT "books_author"."id", "books_author"."name", "books_author"."gender" FROM "books_author" INNER JOIN "books_book" ON ("books_author"."id" = "books_book"."author_id") WHERE "books_book"."title" = '''book1'''