リレーションの理解が悪くて酷いコードを組んでいて、それを直した話。
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が良いらしい。