Django DB イベントのフックを実装します。
DB イベントのフックとは、例えば、特定のテーブルに対してインサートした時に、事前に登録しておいた、コールバック関数を実行するということです。
Django は5年以上使っているのですが、Signals という機能に最近気がつきました。
Signals | Django documentation | Django
覚えていると便利そうですので、どんな感じなのか、実装して動きを見てみたいと思います。
Contents
環境とバージョン
- CentOS 7.5.1804
- Python 3.6.4
- Django 2.1.1
インストール
Django DB イベントのフックを実装する環境を構築します。
Python
$ sudo yum install https://centos7.iuscommunity.org/ius-release.rpm $ sudo yum install python36u python36u-libs python36u-devel python36u-pip
Python3.6 をインストールします。
Django
$ sudo pip install --upgrade django
Django をインストールします。
実装
環境構築が終わったら、次は実装していきます。
プロジェクトとアプリケーションの作成
$ django-admin startproject db_event_hook $ cd db_event_hook/ $ python3.6 manage.py startapp myapp
実装に入る前に、Django の startproject と startapp コマンドで、プロジェクトとアプリケーションの雛形を作成します。コマンドを実行する場所はどこでもOKです。
settings.py
INSTALLED_APPS = [ ... 'myapp.apps.MyappConfig', ]
次に settings.py でプロジェクト全体の設定を行いますが、今回は、INSTALLED_APPS に startapp で作成したアプリケーションを指定するだけです。
models.py
from django.db import models class MyModel(models.Model): col = models.IntegerField()
models に DB を定義します。フック対象とするテーブルです。
apps.py
from django.apps import AppConfig class MyappConfig(AppConfig): name = 'myapp' def ready(self): from myapp import signals
startapp で作成された apps.py に、これから実装する signals をインポートします。
signals.py
from django.db.models.signals import post_save from django.dispatch import receiver from myapp.models import MyModel @receiver(post_save, sender=MyModel) def my_model_post_save_event_handler(sender, instance, **kwargs): print('my_model_post_save_event_handler!!')
今回のメイン関数 signals.py を作成します。
receiver デコレータに、フックしたいイベントと監視対象のテーブルを指定し、コールバック関数を作成します。今回は、書き込み処理後のイベント (post_save) をフックしています。
動作確認
実装が終わったら、動作確認をします。
DB マイグレーション
$ python3.6 manage.py makemigrations Migrations for 'myapp': myapp/migrations/0001_initial.py - Create model MyModel $ python3.6 manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, myapp, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying myapp.0001_initial... OK Applying sessions.0001_initial... OK
まずは、DB マイグレーションを行い、models.py の定義を DB に反映します。
Django シェル
$ python3.6 manage.py shell Python 3.6.4 (default, Dec 19 2017, 14:48:12) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from myapp.models import MyModel >>> MyModel.objects.create(col=1) my_model_post_save_evnet_handler!! <MyModel: MyModel object (1)> >>>
それでは、実際に動作確認していきます。いつもは、デバッグサーバの runserver を起動して、ブラウザからの動作確認ですが、今回は、Django shell を使って、動作確認したいと思います。
Django shell で、フック対象テーブルのレコードを作成(7行目)し、signals.py で実装したコールバック関数が実行される(8行目)のを確認できると思います。
まとめ
Django DB イベントのフック Signals を実装しました。
DB イベントのフックポイントは、他にもたくさん用意されており、様々なシーンで使えそうです。また、Signals は、DB だけでなく、Request のフックポイントや、さらに、独自に Signal を作ることもできそうです。
models.py など、既存ソースコードに手を入れることなく、非同期で処理を追加できるのも便利でいいですね!