Django adminカスタマイズ : list viewで外部テーブルのカラムを使ってソートする

やりたい事

MemoモデルはフィールドとしてUserモデルへの外部キーを持っている。
MemoAdminのリスト表示では、テーブルヘッダのユーザの項目をクリックしたらユーザ名でソートして欲しい、というケース。

その場合、

    list_display = ['user', 'title']

で良いのかなと思いきや、userのpk(user.id)でソートされてしまっているようだった。

調べてみた

結果、カスタムフィールドの属性 admin_order_field にソートしたいカラムを指定すれば良いことが分かった。

http://stackoverflow.com/questions/2168475/django-admin-how-to-sort-by-one-of-the-custom-list-display-fields-that-has-no-d

試しに実装してみたサンプルコードは以下の通り。

サンプルコードにも書いてあるけど、検索対象フィールドの指定は

    search_fields = ['foreign_key__related_fieldname']

これで出来るので、ソートでも同じようにやろうとして一度躓く人はそこそこいるんじゃないかな。

クエリの最適化

カスタムフィールドで obj.user.username の様に外部テーブルを参照すると、レコード毎に一つクエリを発行していた。
サンプルコードでは、 queryset() をオーバーライドして select_related() で参照するフィールドを指定してやることで、リスト表示に必要な情報は一つのクエリにまとめることが出来た。

この部分

    def queryset(self, request):
        qs = super(MemoAdmin, self).queryset(request)
        qs = qs.select_related('user__username')
        return qs

ドキュメントあった

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display

モデル自身に定義してもいいのかー。ModelAdminに書くやり方しか知らなかった。