Как установить время истечения срока действия значения поля?

У меня есть модель User вот такая:

class User(AbstractBaseUser, PermissionsMixin):
    myfield_choices = (('foo', 'First value'), ('bar', 'Second value')

    myfield = CharField(choices=choices)
    
    objects = BaseUserManager()

Я устанавливаю в поле myfield значение foo. После я хочу установить в поле myfield значение bar на 1 день и по истечении этого срока значение станет foo снова автоматически.
Как я могу установить это время истечения? Есть ли какие-нибудь встроенные инструменты django для этого?

Я предполагаю, что вы немного новичок в Django, поэтому я посоветую решение, не основанное на очереди задач (как celery или dramatiq).

Для ясности предположим, что мы реализуем ban_user и unban_user механику. Сначала мы должны обновить модель User:

class User(AbstractBaseUser, PermissionsMixin):
    class BanStatus(models.TextChoices):
        ACTIVE = "ACTIVE", "ACTIVE"
        BANNED_FOR_DAY = "BANNED_FOR_DAY", "BANNED_FOR_DAY"

    ban_status = models.TextField("Ban status", choices=BanStatus.choices)
    banned_for_day_at = models.DateTimeField("Banned for day at", null=True)

Тогда мы должны реализовать точную механику. Я бы предпочел сделать это в каком-нибудь your-app-name/logic/ban_user.py файле:

from django.utils import timezone
# And also an import of User.


def ban_user_for_day(user: User):
    user.ban_status = User.BanStatus.BANNED_FOR_DAY
    user.banned_for_day_at = timezone.now()
    user.save()


def revoke_ban_user_for_day(user: User):
    user.ban_status = User.BanStatus.ACTIVE
    user.banned_for_day_at = None
    user.save()

Таким образом, мы можем вызывать ban_user_for_day из представлений. Что касается revoke_ban_user_for_day, то самый простой способ организовать его вызов - это Command, который можно вызывать периодически, например, с помощью cron. Вы можете разместить его в your-app-name/management/commands/revoke_ban_user_for_day.py

from django.core.management import BaseCommand
from django.utils import timezone
# And also imports of User and revoke_ban_user_for_day.


class Command(BaseCommand):
    def handle(self, *args, **options):
        user_list_for_revoke = User.objects.filter(
            ban_status=User.BanStatus.ACTIVE,
            banned_for_day_at__lte=timezone.now() - timezone.timedelta(days=1)
        ).all()

        for user in user_list_for_revoke:
            revoke_ban_user_for_day(user)

Вот и все. Если бы вы настроили этот Command на выполнение раз в минуту, то задержка между моментом, когда User должен быть разбанен, и моментом, когда он будет фактически разбанен, была бы не более 1 минуты.

Вернуться на верх