Django の extra で MySQL の DATE_FORMAT 関数が使えないときの対応

発生した問題

  • 以下のようなSQLを投げて データを取得しようとしました。
models.User.objects.extra(
    where=["DATE_FORMAT(created, '%Y-%m-%d') = %s"],
    params=['2019-03-13']
)

DjangoのORMが分からない方向けにSQLだと以下のようなSQLに相当します。

SELECT *
  FROM users
 WHERE DATE_FORMAT(created, '%Y-%m-%d') = '2019-03-13'
  • 以下のエラーが出て 続行が出来なかった。
Traceback (most recent call last):
  〜中略〜
  File "/usr/local/lib/python3.6/site-packages/MySQLdb/cursors.py", line 238, in execute
    query = query % args
ValueError: unsupported format character 'Y' (0x59) at index 103

解決方法

  • 文字列フォーマットを使用する時に % をそのまま残したい場合は %% と連続して記述する。
models.User.objects.extra(
    where=["DATE_FORMAT(created, '%%Y-%%m-%%d') = %s"],
    params=['2019-03-13']
)

原因

Pythonには 文字列フォーマット というものが組み込まれています。

"%s" % 'test'

上記のように書くことで Python{%s} の部分に test という値を 文字列として展開 します。

これは Pythonでいう所の 文字列内で変数を展開する手法の一つです。

代表的なものとして %s , %d などがあります。

ここでのエラーは %Y っていうのはサポートしてませんよ というエラーでした。

余談

%s の書き方は今ではあまり好まれておらず、 以下のように str.format() 関数を使って実装するのが良いとの事らしいです。

`{}`.format('test')  

確かに便利ですよね。 format()