Djangoで、ログイン・ログアウトの履歴を管理する
概要
Djangoにデフォルトで付属したadmin管理サイトやUserモデルは大変便利です。
今回は、このUserモデルのログイン・ログアウトの履歴を管理するアプリケーションを作ります。ちょっとした出勤簿にも使えるかもしれません。
以下のような感じで表示できます。ログイン・ログアウトを管理するモデルを作成します。
ソースコード
models.py
です。
from django.conf import settings
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.db import models
from django.dispatch import receiver
from django.utils import timezone
class AttendanceRecord(models.Model):
"""出勤簿"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name='ユーザー', on_delete=models.PROTECT)
login_time = models.DateTimeField('ログイン時刻', blank=True, null=True)
logout_time = models.DateTimeField('ログアウト時刻', blank=True, null=True)
def __str__(self):
login_dt = timezone.localtime(self.login_time)
return '{0} - {1.year}/{1.month}/{1.day} {1.hour}:{1.minute}:{1.second} - {2}'.format(
self.user.username, login_dt, self.get_diff_time()
)
def get_diff_time(self):
"""ログアウト時間ーログイン時間"""
if not self.logout_time:
return 'ログアウトしていません'
else:
td = self.logout_time - self.login_time
return '{0}時間{1}分'.format(
td.seconds // 3600, (td.seconds // 60) % 60)
@receiver(user_logged_in)
def user_logged_in_callback(sender, request, user, **kwargs):
"""ログインした際に呼ばれる"""
AttendanceRecord.objects.create(user=user, login_time=timezone.now())
@receiver(user_logged_out)
def user_logged_out_callback(sender, request, user, **kwargs):
"""ログアウトした際に呼ばれる"""
records = AttendanceRecord.objects.filter(user=user, logout_time__isnull=True)
if records:
record = records.latest('pk')
record.logout_time = timezone.now()
record.save()
AttendanceRecord
が、ログイン・ログアウトを管理するモデルです。
フィールととして、どのユーザーか、ログイン時間、ログアウト時間の3つのフィールドをもたせます。
__str__
では、ユーザー名 - ログイン日付 - 何時間ログインしたかを表示させます。
__str__で日付をタイムゾーン対応
基本的に、settings.py
でタイムゾーンを設定していれば、それらは自動的に変換されます。フォームやテンプレートで表示する際には基本的に問題ありません。
が、__str__
内で文字列として日付を埋め込むような場合は別です。これは手動で変換する必要があります...とはいっても、Django側に変換するためのユーティリティ関数があるのでそれが使えます。timezone.localtime
です。
login_dt = timezone.localtime(self.login_time)
日付の差を取得する
get_diff_time()
は、何時間ログインしたかを返すメソッドですね。○時間○分といった形で返すには、このような書き方で充分でしょう。あくまで日付の差だけわかりゃいいので、こちらはタイムゾーン関連は考慮しなくて良いです。
def get_diff_time(self):
"""ログアウト時間ーログイン時間"""
if not self.logout_time:
return 'ログアウトしていません'
else:
td = self.logout_time - self.login_time
return '{0}時間{1}分'.format(
td.seconds // 3600, (td.seconds // 60) % 60)
ユーザーのログイン・ログアウトイベントをキャッチする
ユーザーのログインとログアウトをキャッチする必要があります。
Djangoにおいては、モデルの保存...saveメソッドの前後等で何か処理をしたいならばシグナルという機能が便利です。
幸いにも、Userモデルのログイン・ログアウトのシグナルは素直に提供されているので、それを使うことができます。@receiver(user_logged_in)
と、@receiver(user_logged_out)
ですね。
@receiver(user_logged_in)
def user_logged_in_callback(sender, request, user, **kwargs):
"""ログインした際に呼ばれる"""
AttendanceRecord.objects.create(user=user, login_time=timezone.now())
@receiver(user_logged_out)
def user_logged_out_callback(sender, request, user, **kwargs):
"""ログアウトした際に呼ばれる"""
records = AttendanceRecord.objects.filter(user=user, logout_time__isnull=True)
if records:
record = records.latest('pk')
record.logout_time = timezone.now()
record.save()
ログアウト時にやることは、ログイン時に作られたAttendanceRecord
モデルインスタンスを探すことです。
具体的な条件としては、
- ユーザーが自分で
logout_time
がまだnull
のとき
です。
これはfilter(user=user, logout_time__isnull=True)
で判断することができます。