Djangoで開発していると、「このイベントが発生したら、どんな動作を実行したいか」という状況が本当に頻繁に発生します。例えば、特定のモデルが保存されるたびにログを残したり、ユーザープロフィールが更新されたときに他の関連データを自動的に更新したいときがあります。そんな時に便利なのがDjango Signalsです。Signalsはイベントベースの構造を作るため、コード間の結合を緩めながらも必要な作業を実行できるようにしてくれます。
このポストでは、Django Signals の中でも最もよく使用されるpre_save
とpost_save
信号を中心に、どのように活用できるのかを見ていきましょう。

1. Django Signals 概要
Django Signalsは、アプリケーションで特定のイベントが発生したときに、あらかじめ定義された関数を自動的に呼び出す機能です。開発者自身が信号を定義することもできますが、Djangoではpre_save
、post_save
、pre_delete
、post_delete
などの信号を基本的に提供しています。これらの信号は、モデルインスタンスが保存または削除されるときに発生するため、データベースと関連する動作をトリガーする際に特に便利です。
Tip: Signalsを設定する際は、信号の種類と信号を受け取る対象(モデル)を明確に指定する必要があります。そうしないと、予期しないエラーを避けられません。
2. pre_save
と post_save
の違い
pre_save
: モデルインスタンスがデータベースに保存される前に実行されます。post_save
: モデルインスタンスがデータベースに保存された後に実行されます。
この2つの信号はタイミングが異なるため、どのような作業をするかに応じて適切な信号を選択する必要があります。例えば、保存される前に値を変更したい場合はpre_save
を、保存が終わった後に別の作業をしたい場合はpost_save
がより適しているでしょう。
3. pre_save
使用例
ここではpre_save
信号を通じて、実際にどのような作業を行えるのかを見ていきましょう。たとえば、ユーザー名を保存する前に自動的に小文字に変換する作業を行いたいと仮定します。
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import UserProfile
@receiver(pre_save, sender=UserProfile)
def lowercase_username(sender, instance, **kwargs):
instance.username = instance.username.lower()
上記のコードでは、@receiver
デコレーターがpre_save
信号とlowercase_username
関数をつなげています。これでUserProfile
モデルインスタンスが保存される前にこの関数が自動的に呼び出され、username
フィールドが小文字に変換されます。
Tip:
pre_save
信号はデータがデータベースに入る前に処理されるため、保存前のデータ検証やフィールド値の変換に便利です。
pre_save
でよくあるミス
pre_save
信号を使用する際、最もよくあるミスの一つはsave
メソッドを再度呼び出すことです。たとえば、保存前に特定のフィールドを更新する際に、間違ってinstance.save()
を再度呼び出してしまうと、無限ループに陥る可能性があります。信号処理関数内部でsave()
を再び呼び出さないように注意しましょう。
4. post_save
使用例
次はpost_save
信号を使用してみましょう。例えば、ユーザーが会員登録をするときに、歓迎のメールを送信する機能を実装したいとしましょう。この際post_save
信号が非常に便利です。
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from .models import UserProfile
@receiver(post_save, sender=UserProfile)
def send_welcome_email(sender, instance, created, **kwargs):
if created: # 新しく作成された場合のみメールを送信
send_mail(
'Welcome!',
'Thank you for signing up!',
'from@example.com',
[instance.email],
fail_silently=False,
)
ここではcreated
という引数を使って、オブジェクトが新たに生成された場合のみメールを送信するように設定しました。post_save
信号はデータベースに保存が完了した後に動作するため、データを確認し、追加の後処理を行うのに適しています。
post_save
での活用例: 関連モデルの更新
post_save
信号は主に「モデルが保存された後に追加で作業をしなければならないとき」に使用されます。例えば、ブログ記事が保存された後に自動的にタグやカテゴリーの数を更新したり、商品在庫が変更されるときにログを残したい場合に利用できます。
from .models import BlogPost, Category
@receiver(post_save, sender=BlogPost)
def update_category_count(sender, instance, created, **kwargs):
if created:
category = instance.category
category.post_count = BlogPost.objects.filter(category=category).count()
category.save()
上記の例はBlogPost
インスタンスが新たに保存されるたびに、関連するカテゴリーのpost_count
を更新する方法です。post_save
信号を使用すれば、データを保存した後に関連するデータを動的に変更でき、非常に便利です。
5. pre_save
と post_save
を使用する際の注意点
- 無限ループの回避: 信号処理関数内で
save()
を再呼び出ししないように注意しましょう。save()
を呼び出すと自動的にpre_save
とpost_save
信号が発生し、無限ループを引き起こす可能性があります。 - 条件付き処理: 特定の条件でのみ信号を処理するよう設定しましょう。例えば、
post_save
でcreated
引数を使うことで、オブジェクトが新しく生成された場合と更新された場合を区別できます。 - 信号の登録位置: 信号を登録する位置も重要です。一般的には
apps.py
ファイルのready()
メソッド内に信号を登録するか、別のsignals.py
ファイルを作成して管理するのが良いでしょう。信号を複数の場所で登録すると、予期しない動作が発生する可能性があります。
まとめ
Djangoのpre_save
とpost_save
信号は、データ保存前後にさまざまな作業を行うことができます。単にデータを保存するだけでなく、保存前にデータを検証したり、保存後に他のモデルとの関係を更新したりするなど、信号を活用すれば開発効率とコードの柔軟性を大いに高めることができます。
次回の記事ではpre_delete
とpost_delete
信号について学び、削除時にできるさまざまな活用法を取り上げる予定です。SignalsはDjango開発をより興味深く効率的にするツールですので、状況に応じてうまく活用してみましょう!
Add a New Comment