リレーションの理解が悪くて酷いコードを組んでいて、それを直した話。
article
key | title | content | author_key |
---|---|---|---|
1 | DjangoでHello World作ってみた | 出来なかった! | 1 |
author
key | name |
---|---|
1 | shigepon |
こんなテーブルがあって、author.keyとarticle.author_keyが繋がっている場合に ArticleモデルをDjango使い始めの時はこんな風に作ってた
# models.py
class Article(models.Model):
key=models.IntegerField(primary_key=True)
title=models.TextField()
content=models.TextField()
author_key=models.IntegerField()
def author(self):
return Author.objects.get(key=self.author_key)
class Author(models.Model):
key=models.IntegerField(primary_key=True)
name=models.TextField()
いやー改めて見ると酷い。Articleを100個読み込んで、そのAuthorを出力するだけで、100回以上dbにアクセスしないといけない。という訳でこの酷いコードからリレーションを貼って、さらにdbアクセスが少なくなるように修正したらこうなった
参考:
- Model field reference | Django documentation | Django
- QuerySet API reference | Django documentation | Django
# models.py
class Article(models.Model):
key=models.IntegerField(primary_key=True)
title=models.TextField()
content=models.TextField()
author=models.ForeignKey('Author', db_column='author_key', to_field='key', related_name='articles')
class Author(models.Model):
key=models.IntegerField(primary_key=True)
name=models.TextField()
こんな感じで適切にForeignKeyフィールドを設定することで、上の構成のテーブルにもリレーションを貼ることが出来た。db_columnでArticle側のリレーションキーになるカラムを指定して、to_fieldでAuthor側のリレーションキーになるカラムを指定する。related_nameは指定しないとエラーが出た。
んで、dbアクセスがなるべく少なくなるように(sqlでjoinを組んでくれるように)views.pyを書いてみると
# views.py
from app.models import Article
def index(request):
articles = Article.objects.select_related('author').all()
...
みたいにselect_relatedメソッドを使うとjoinしてくれた。prefetch_relatedってメソッドでも同じように出来る(ちょっと役割は違う)らしいが試していない。ざっくりドキュメントを読むと、ForeignKeyと1対1リレーションにはselect_relatedが良く、Many to ManyやMany to Oneにはprefetch_relatedが良いらしい。