- ユーザーの年齢は常に最新の値が欲しい!
このような方に向けて書きました。
本記事では最新の年齢を取得し続けるための方法を解説します。
年齢をデータベースで年齢を管理したい場合にはage
フィールドを用意することを考えてしまいがちですが、これでは時間と共に自動的に更新されません。(登録時の年齢しか登録されない)
年齢情報が欲しいケースは「現段階の年齢」が欲しい場合がほとんどなので、今回の動的に実装する方法は便利です。
最新の年齢を取得し続ける2つの方法
実装の方針は次の2パターンです。
- 年齢をプロパティとして設定
- 定期的に
age
フィールドを更新
基本的には❶の方法が推奨ですが、❷の方法も併せて解説します。
年齢をプロパティとして設定
age
フィールドは定義せずに、プロパティとして年齢を計算する方法です。
from django.utils import timezone
class Child(models.Model):
# 生年月日
birthdate = models.DateField(
verbose_name="生年月日",
blank=True,
null=True,
)
@property
def age(self):
# 今日の日付を取得
today = timezone.now().date()
# 年齢を計算
age = today.year - self.birthdate.year
# 誕生日が来ていない場合は1引く
if (today.month, today.day) < (self.birthdate.month, self.birthdate.day):
age -= 1
return age
フィールドとしては不変の誕生日を持たせておいて、都度年齢を計算します。
メリットは以下の通り。
- データベースの更新が不要
- アクセスのたびに正確な年齢が計算できる
- 年齢と誕生日の情報が分離できる
ここではプロパティとして定義したので、以下のようにして呼び出せるようになります。
# 例: Childインスタンスのageプロパティにアクセス
child = Child.objects.get(id=1)
# メソッド呼び出しではなく、属性としてアクセス
print(child.age)
定期的にage
フィールドを更新
データ管理上の理由でage
フィールドが欲しい場合もある場合には、定期的にデータベースを更新する方法もあります。
まずはage
フィールドを設定。
from django.conf import settings
from django.db import models
from django.core.validators import MinValueValidator
class Child(models.Model):
# 年齢
age = models.PositiveIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(0)],
help_text="子供の年齢(自動更新されます)",
)
その上でカスタムコマンドを定義します。
from django.core.management.base import BaseCommand
from django.utils import timezone
from myapp.models import Child
class Command(BaseCommand):
help = "Update ages for all children"
def handle(self, *args, **kwargs):
today = timezone.now().date()
children = Child.objects.filter(birthdate__isnull=False)
for child in children:
age = today.year - child.birthdate.year
if (today.month, today.day) < (child.birthdate.month, child.birthdate.day):
age -= 1
child.age = age
child.save()
self.stdout.write(self.style.SUCCESS("Successfully updated ages"))
これを実行する手段としては、大きく次の3つの方法があります。
- Djangoの管理コマンドを利用
- Celeryなどのタスクキューを利用
- データベーストリガーを利用
これらの具体的な方法は、後日記事で方法をご紹介する予定です。
補足:アプリに年齢制限を設ける
たとえば年齢制限をつけたい場合には、ageフィールドを設置する必要があります。
この場合には以下のようなclean
メソッドを追記することがあります。
def clean(self):
if self.age and self.age < 5:
raise ValidationError("5歳未満の子供は登録できません。")
まとめ
年齢はプロパティにすることで、常に最新の年齢を取得可能です。
やむなくage
フィールドを用意したい場合には、カスタムコマンドを用意して定期実行という流れになります。
カスタムコマンドについては、以下の記事で説明しました。
» 参考:【初心者でも簡単】Djangoカスタムコマンド入門
コメント