DjangoのSystem check frameworkでCloud Spanner非対応のモデルフィールドを検出してみる


Qiita Django Advent Calendar 2021 8日目は、DjangoのSystem check frameworkを使ったちょっとしたTipsを紹介します。

DjangoにはSystem check frameworkという、ソースコードの静的解析を行うための仕組みがあります。 System check frameworkは以下のタイミングで実行されます。

System check frameworkの基本的な使い方については、以下公式ドキュメントを参照してください。

システムチェックフレームワーク | Django ドキュメント | Django

この仕組みを利用して、django-google-spannerを使ってデータベースバックエンドにCloud Spannerを利用しているプロジェクトで便利そうな静的解析を加えてみます。

Cloud Spannerでは、django.db.models.JSONFieldに対応する型がないため、django-google-spannerを使ったプロジェクトでは使うことができません。 誤って使っても、開発環境で本物のCloud Spannerインスタンスを使っていればすぐ気づけますが、(コストを抑えたい、実行速度が遅いからなどの理由で)SQLiteを使っている場合はエラーになりません。1 開発環境でSQLiteを使っていても間違いにすぐ気付けるように、System check frameworkを利用してdjango.db.models.JSONFieldを使っているモデルフィールドを検出する仕組みを作ってみましょう。

この記事のサンプルコードで使っているDjangoのバージョンは3.2.10です。

exampleというDjangoアプリケーションの下にチェック関数を作ります。 まず、example/checks.pyに以下の内容を書きます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from itertools import chain

from django.apps import apps
from django.core.checks import Error, register
from django.db.models import JSONField


@register()
def fields_not_supported_by_cloud_spanner_check(app_configs, **kwargs):
    errors = []
    app_configs = app_configs if app_configs else apps.get_app_configs()
    # チェック対象のアプリケーションに定義されているモデルを抽出
    models = chain.from_iterable(
        (app_config.get_models() for app_config in app_configs)
    )
    # モデルに定義されているフィールドを抽出
    fields = chain.from_iterable((model._meta.fields for model in models))
    # django.db.models.JSONFieldを使っているフィールドを抽出
    json_fields = (field for field in fields if isinstance(field, JSONField))
    for json_field in json_fields:
        errors.append(
            Error(
                "Cloud Spannerで非対応のフィールドクラスが使われています",
                hint="django.db.models.JSONField以外のフィールドクラスを使ってください",
                obj=json_field,
                id="example.E001",
            )
        )
    return errors

example/apps.pyは以下の内容に編集して、上記のチェック関数を読み込むようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from django.apps import AppConfig


class ExampleConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "example"

    # これを追記
    def ready(self):
        from .checks import fields_not_supported_by_cloud_spanner_check

これで独自のチェック関数が使えるようになりました。 django.db.models.JSONFieldを使ったフィールドがあるモデルを追加して、checkコマンドを実行してみましょう。以下のようなエラーメッセージが出力されるはずです。

1
2
3
4
5
6
7
8
$ ./manage.py check
SystemCheckError: System check identified some issues:

ERRORS:
example.Example.json_value: (example.E001) Cloud Spannerで非対応のフィールドクラスが使われています
	HINT: django.db.models.JSONField以外のフィールドクラスを使ってください

System check identified 1 issue (0 silenced).


  1. ローカルのエミュレータを使えば解決しそうですが、django-google-spanner==3.0.0時点では使えないと考えたほうがよさそうです。詳しくはアドベントカレンダーの6日目に書いた記事を読んでください。 ↩︎


comments powered by Disqus