🚄

【Django】ChoiceField/ModelChoiceFieldの表示速度比較

2021/09/07に公開

はじめに

DjangoでWebアプリを開発中、Formでユーザにデータを選択させたい場合にChoiceFieldとModelChoiceFieldどちらを使うべきかが気になりました。
公式ドキュメントでは、ModelChoiceFieldについて

Note that the default widget for ModelChoiceField becomes impractical when the number of entries increases. You should avoid using it for more than 100 items.

とあります。
ChoiceFieldにはこの注意書きは無いので、一見するとModelChoiceFieldの方が多量のデータがある場合著しくパフォーマンスが下がるような気がします。
実際のところはどうなのか、ChoiceFieldとModelChoiceFieldそれぞれを利用したフォームのブラウザでのレスポンス速度を検証しました。

検証環境

  • Debian GNU/Linux 11 (bullseye) (Docker)
  • Python 3.9.7
  • Django 3.2.7
  • Google Chrome 93.0.4577.63
  • DB:SQLite3

検証方法

面倒な人は結果まですっ飛ばしてください。

Choice/ModelChoiceの両者ともに、10000件のデータを設定したフォームがあるページの読み込み速度を、ChromeDeveloperToolsのNetworkタブで計測しました。
データはそれぞれ次のように設定しました。import文等は省略しています。

ChoiceField

forms.py
choice_tuple = (
    ("1", "選択肢1"),
    ("2", "選択肢2"),
    # 中略
    ("9999", "選択肢9999"),
    ("10000", "選択肢10000"),
)

class SampleChoiceForm(forms.Form):
    choice = forms.ChoiceField(choices=choice_tuple)

ModelChoiceField

models.py
class SampleModel(models.Model):
    choice = models.CharField(verbose_name="choice", max_length=255)
    
    def __str__(self) -> str:
        return self.choice
forms.py
class SampleChoiceForm(forms.Form):
    choice = forms.ModelChoiceField(queryset=SampleModel.objects)

SampleModelテーブルのchoiceには"選択肢1"~"選択肢10000"までの文字列が入れてあります。

共通部分

Viewはテンプレートとフォームを指定しているだけのごく簡素なFormView、
テンプレートは{{ form }}という一文のみの、本当にただフォームを表示するだけのテンプレートです。

結果

ほぼ変わりませんでした。
以下がリクエストを送ってからのそれぞれのレスポンス速度[秒]です。
Chrome Developer ToolsのNetworkタブにて、各フィールド5回計測しました。
1回のアクセスごとにキャッシュは削除しています。

ChoiceField ModelChoiceField
3.95 4.15
3.55 3.78
3.57 3.81
3.55 3.76
3.61 3.80

まとめ

意外にも両者実行速度はほぼ変わりませんでした。平均すると0.2秒ほどChoiceFieldの方が速いですが、ほぼ誤差の範囲内でしょう。Djangoのソースコードを追ったわけではないので内部挙動がどうなっているかは分かりませんが、恐らくリクエスト→Djangoでリクエストを処理→HTMLを生成してレスポンスというような流れになっているはずなので、この約0.2秒はModelChoiceFieldで指定されたSampleModelのクエリを発行している時間と思われます。案外早いですね。ただし、今回はDBにDjangoデフォルトのSQLite3を使用しましたが、他のDBを使用した場合に違いが出る可能性はあります。
とはいえ、実際問題1回のレスポンスに3秒以上もかかってたらやってられないですし、実運用面では多量のリクエストが送られる場合も考えられます。なるべくなら処理は軽くしておいた方が良いですし、公式ドキュメントの言う通りModelChoiceFormは100件以上のクエリセットでは使わない方が良いでしょう。
選択肢が不変ならChoiceField、変更される可能性がある・他のテーブルと密接に関わるならModelChoiceField、データが100件を超すなら(Model)ChoiceFieldではない他の方法を検討する、が適切かと思いました。

Discussion