アイリッジ開発者ブログ

アイリッジに所属するエンジニアが技術情報を発信していきます。

Django2.2から3.2へのバージョンアップで発生するクエリ変更への対応

f:id:iridge-tech:20211214195500p:plain

開発部第2グループの佐藤です。

Django 2.2.23から3.2.6へのバージョンアップしたところ、ORMが生成するクエリが一部変更されていました。調査した変更内容と対応方法を共有します。使用しているDBはMySQLです。

起こったこと

booleanフィールドでfilterする際生成されるクエリが変更されていました。 MyModel.objects.filter(is_hoge=True) から出力されるクエリは以下の通りです

# サンプルのclass
class MyModel(TimeStampedModel):
    is_hoge = models.BooleanField()
    is_piyo = models.BooleanField()

# クエリ例
MyModel.objects.filter(is_hoge=True)
MyModel.objects.filter(is_piyo=False)
// 今までの出力
SELECT * FROM app_my_model WHERE is_hoge=1;
SELECT * FROM app_my_model WHERE is_piyo=0;

// アップデート後の出力
SELECT * FROM app_my_model WHERE is_hoge;
SELECT * FROM app_my_model WHERE NOT is_piyo;

MySQLではBooleanFieldはTINYINT型のカラムとなり、テーブルに格納される値はTrueの場合は1、Falseの場合は0となります。

WHERE is_hoge の状態でも今までと同じ検索結果が出力されますが、is_hogeがTINYINT型であるため、-128〜127の0を除いた値(もしくは1〜255)の範囲検索が実行され、INDEXも効かなくなります。

リリースノートには変更の記載はなく、Djangoのissueが発行されています。https://code.djangoproject.com/ticket/32691

対応

クエリを今まで通り is_hoge=1is_piyo=0 と出力させるための修正例は以下の通りです。

from django.db import models

MyModel.objects.filter(is_hoge=models.Value(1))
MyModel.objects.filter(is_piyo=models.Value(0))

なかなか発見しづらい変更内容です。皆様もご注意ください。