【Django】【Webアプリ開発】通知機能を追加しスマホにお知らせする

投稿日 2019年10月21日 >> 更新日 2023年3月2日

今回はDjangoアプリに通知機能を追加し、Webサイト上で何らかの動きがあったらスマホでお知らせしてくれるといった開発をしていきたいと思います。

前提として、ここではGmailを使用して作業を行うのでアプリパスワードを取得済みである事です。

もちろんメールサーバーを変えて独自の方法でやるのも自由です。

簡単な説明ですがGmailのアプリパスワードの取得方法はこちらをご覧ください。

あとはDjangoアプリの準備ですが、簡単なブログアプリを作成してどのような動きをするのか実践的に試して行きます。

私が過去に投稿した記事で「【Django】Positive Integer Fieldから閲覧数(PV)を取得して一覧表示させる
というのがあるので、よろしければその記事内の「閲覧数取得用のフィールドを作成」まで終わりましたら進めてください。

このような一覧表示から始めていきたいと思います。

※「一覧」下の日付は後付けです。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.8
pip 9.0.1
使用ライブラリ ライセンス
Django==2.2.6 BSD

クリックされる度に通知が届く

まず最初の通知機能は、クリックされたら通知を送る。

そしてどの記事がクリックされたかを記載する。

です。

Djangoには様々なメール送信機能がありますがその中でよく取り上げられている「send_mail」というメソッドを使用します。

メールサーバーを使えるようにする為にDjangoの設定ファイル「settings.py」にて記述していきます。

# project/project/settings.py

STATIC_URL = '/static/'

EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'アドレス@gmail.com'
EMAIL_HOST_PASSWORD = 'アプリパスワード'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

次にviews.pyを編集します。

設定ファイルで指定した「EMAIL_HOST_USER」のアドレスを読み込むようにしましょう。

# project/blog/views.py

from django.shortcuts import render, get_object_or_404
from .models import Blog

from django.conf import settings  \# 追加
import datetime  \# 追加
from django.core.mail import send_mail   \# 追加


""" 一覧表示"""
def index(request):
    time = datetime.datetime.now()   \# 日付用
    blog = Blog.objects.order_by('-id')
    return render(request, 'blog/index.html',
                    {'blog': blog, 'time': time })


""" 記事詳細"""
def detail(request, blog_id):
    blog = get_object_or_404(Blog, id=blog_id)
    blog.views += 1
    blog.save()
    """ 追加"""
    subject = 'Djangoアプリから通知'
    massege = '{}が読まれました'.format(blog)
    from_mail = []
    recipient = [settings.EMAIL_HOST_USER]
    send_mail(subject, massege, from_mail, recipient)
    return render(request, 'blog/detail.html', {'blog': blog })

「send_mail」は4つの引数が必須となっていて、「send_mail(件名, 内容, アドレス, [受信者リスト])」のような記載でメールが送られるようになっています。

一覧表示にて日付を追加したので、index.htmlも編集します。

<!-- project/blog/templates/blog/index.html -->

{% extends 'blog/base.html' %}

{% block content %}

    <h1>一覧</h1>
    <!-- 日付-->
    {{ time }}
    .......
    .......

では実際に機能するのか試してみましょう。

記事3の詳細をクリックしてみます。

詳細が開かれると同時にメールが受信されれば成功です。

特定の数字に達したら通知

次に行う作業は、記事の総閲覧数が特定の数に達したらお知らせするような設定をしていきます。

例えば、3つある記事すべて合わせて10回クリックされたらといったような目標値設定型などがいいかと思います。

これを行うにはviews.pyのdetail関数で定義した「send_mail」をindex関数に移すような形で編集していきます。

# project/blog/views.py

.......

def index(request):
    time = datetime.datetime.now()
    blog = Blog.objects.order_by('-id')
    """ 追加"""
    value = [value.views for value in blog]
    value_sum = sum(value)

    if value_sum == 10:
        subject = 'Djangoアプリから通知'
        massege = '{}PV達成しました!'.format(value_sum)
        from_mail = []
        recipient = [settings.EMAIL_HOST_USER]
        send_mail(subject, massege, from_mail, recipient)

    return render(request, 'blog/index.html',
                    {'blog': blog, 'time': time })


def detail(request, blog_id):
    blog = get_object_or_404(Blog, id=blog_id)
    blog.views += 1
    blog.save()
    return render(request, 'blog/detail.html', {'blog': blog })

index関数に追加した部分は以下です。


value = [value.views for value in blog]
value_sum = sum(value)
if value_sum == 10:
    subject = 'Djangoアプリから通知'
    massege = '{}PV達成しました!'.format(value_sum)
    from_mail = []
    recipient = [settings.EMAIL_HOST_USER]
    send_mail(subject, massege, from_mail, recipient)

[value.views for value in blog]はリスト内包表記でそれぞれの記事に格納された値を[値, 値, 値]のように取り出しています。

取り出したリストをsum()メソッドで合計を割り出し条件分岐で比較します。

条件分岐で比較されている値が目標値です。

あとは先ほどと同じように記述するだけで、また違った通知を受けられるようになります。

では適当にクリックして挙動を試してみましょう。

通知がきたら成功です。

ただ1つ問題があって、1度目標値が達成されてしまったらその後の通知はどうなるのだろうか?

ご想像の通り、通知は2度と来ません。

やはり条件分岐で目標値をハードコードしている以上、同じ数字が表れないとクリアできないので他に処置が必用になります。

例えばこのようにするのも1つの手です。


value = [value.views for value in blog]
value_sum = sum(value)
if value_sum == 10:
    subject = 'Djangoアプリから通知'
    massege = '{}PV達成しました!'.format(value_sum)
    from_mail = []
    recipient = [settings.EMAIL_HOST_USER]
    send_mail(subject, massege, from_mail, recipient)
    blog.update(views=0)   \# 記事の閲覧数を0にする

メールが送信されたら閲覧数を0に一括更新です。

これは非常に楽な方法ですが、楽さ故に柔軟性がありません。

理想を言ってしまえば、日付が変わったら0に戻し、且つその日に獲得した総閲覧数をお知らせしてもらうといったことを思い浮かべるはずです。

その理想を叶えるには、Djangoのカスタムコマンドを使うことです。

カスタムコマンドを作成することによって自動化も図れるのでかなり柔軟な開発を実現できます。

ここからの内容は少しばかりUnix系(cronで自動化)に絞られてしまいますがWebサイトを作る上では重宝されると思うのでよろしければお付き合いください。

カスタムコマンドの作成

まずカスタムコマンドを簡単に説明すると、ターミナル上でPythonモジュールを起動させるためのコマンドを追加することです。

Djangoに何かしらのアクションを起こさせる際に「python3 manage.py コマンド」のようなコマンドを打ちますが、その「コマンド」を作成します。

コマンドを新たに追加するには「management/commands」ディレクトリ内でスクリプトファイルを作る必要があります。

# project/blog/

blog
  |--management
  |       |--commands
  |                |--カスタムコマンド.py
  |
  |--migrations
  |--templates
  |--views.py

ではコマンド名を「views_mail.py」にしたいと思います。

ターミナルにてコマンド「python3 manage.py views_mail」が起動されたら、各記事の閲覧数合計がスマホに通知され、その後に各閲覧数は0に更新される。といった内容です。

ではcommandsディレクトリ内のviews_mail.pyファイルを作成します。

# project/blog/management/commands/views_mail.py

from django.core.management.base import BaseCommand
from django.core.mail import send_mail
from django.conf import settings
from blog.models import Blog
import datetime


class Command(BaseCommand):

    def handle(self, *args, **options):
        blog = Blog.objects.all()
        view = [blog_v.views for blog_v in blog]
        blog_view = sum(view)
        time = datetime.date.today()
        subject = 'PV結果'
        massege = '{0}は計{1}PVでした'.format(time, blog_view)
        from_mail = []
        recipient = [settings.EMAIL_HOST_USER]
        send_mail(subject, massege, from_mail, recipient)
        blog.update(views=0)
        self.stdout.write(self.style.SUCCESS('メッセージ Mail'))

BaseCommandクラスは、作成されたコマンドにアクセスするために必要なクラスです。

より詳しい内容は公式ドキュメントへ

クラス定義をし、「def handle」関数を利用するだけでカスタムコマンドの出来上がりです。

内容は、views.pyで定義したコードとほぼ同じになります。

それではさっそく記事閲覧数をいくつかクリックしてから、コマンドを打ってみましょう。

# project/

$ python3 manage.py views_mail
メッセージ Mail

「メッセージ Mail」の応答と同時に通知が届くはずです。

では最後にこのコマンドを日付変更とともに起動するよう自動化します。

ここからはUnix系の話題となってしまいますがご了承ください。

cron(crontab)で自動化

cron(crontab)とは、Unix系オペレーションシステムでの定時実行コマンドで、スケジュール管理などに使われています。

ここでは簡単に進めてしまうので、仮想環境などの構築をされていたらこちらを参考にしながら実行してみてください。

ではcrontabをコピーし、ファイル名を拡張子無しの「cron_test」として保存します。

# home/ubuntu/

$ sudo cp /etc/crontab /etc/cron.d/cron_test

crontabファイル内へ実行命令を記述することもできますが、非推奨となっているのでコピーして「cron.d」ディレクトリ内へ配置しました。

cron_testファイルをvimエディタで開いて記述します。

# home/ubuntu/

$ sudo vi /etc/cron.d/cron_test

ルートディレクトリから実行されるので、Djangoのプロジェクトディレクトリまでのフルパスを記述します。(通常の環境)

時間は0時00分に実行されるよう設定します。

# home/ubuntu/etc/cron.d/cron_test

00  0  *  *  *  root python3 /home/ubuntu/project/manage.py views_mail


vimエディタを閉じるには、キーボード左上の「Esc」を押し「:」に続けて「wq」とすることで保存され終了します。

設定を反映します。

```bash
# home/ubuntu/

$ sudo service cron restart
 * Restarting periodic command scheduler cron
 * Stopping periodic command scheduler cron       [  OK  ]
 * Starting periodic command scheduler cron         [  OK  ]

cronについては【Django】カスタムコマンドを使用しcron(crontab)で登録・削除を自動化のほうでより詳しく説明しているので、よろしければご覧ください。

今回実装したことを応用していけば、さらに面白い開発ができると思われます。

最近では機械学習や深層学習のライブラリが豊富になっているので、上手く組み合わせることによってオンリーワンなWebサイトを構築できるかもしれません。

ぜひ試してみましょう。

それでは以上となります。

最後までご覧いただきありがとうございました。

一覧へ戻る