Как мне запретить пользователю создавать несколько объектов в течение 24 часов в Django

Я пытаюсь запретить пользователям создавать более одного объекта интеллектуального анализа данных в течение 24 часов в моем проекте Django. Я решил использовать сигналы Django, в частности сигнал pre_save, чтобы обеспечить соблюдение этого правила глобально — независимо от того, создается ли объект через панель администратора, представление или конечную точку API.

Вот моя модель майнинга:

class Mining(models.Model):
    mining_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    amount_mine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES)

    duration_seconds = models.PositiveIntegerField(null=True, blank=True)
    verified = models.BooleanField(default=False)

    ip_address = models.GenericIPAddressField(null=True, blank=True)
    hardware_info = models.TextField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.user} mined {self.amount_mine} AFC on {self.created_at.strftime('%Y-%m-%d')}"

Мои сигналы:

from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils import timezone
from datetime import timedelta
from .models import Mining

@receiver(pre_save, sender=Mining)
def prevent_multiple_mining_per_day(sender, instance, **kwargs):
    if not instance.pk:
        last_24_hours = timezone.now() - timedelta(hours=24)
        has_recent = Mining.objects.filter(
            user=instance.user,
            created_at__gte=last_24_hours
        ).exists()
        if has_recent:
            raise ValueError("User has already mined in the last 24 hours.")

Я выбрал signals, потому что хочу, чтобы это ограничение применялось независимо от того, где создается объект — в представлениях, панели администратора или REST API, чтобы обеспечить согласованность во всем приложении.

Даже при наличии сигнала я все равно могу создать несколько объектов майнинга в течение 24 часов из панели администратора Django. Сигнал, похоже, не мешает этому, как ожидалось — ошибка не возникает, и объекты сохраняются.

Причина сбоя в том, что instance.pk будет иметь значение для поля со значением по умолчанию. Значение AutoField не имеет значения по умолчанию: оно равно None, и позже база данных присваивает ему значение. Но UUIDField, таким образом, этого не делает: он с готовностью генерирует UUID.

Но, вероятно, сигналы - не самая лучшая идея. Идея могла бы заключаться в том, чтобы просто запретить пользователям создавать две или более записей за каждый день. Это другое ограничение, поскольку, если следующий день начинается в течение секунды, можно создать две учетные записи в течение одной секунды, а в другом случае для этого может потребоваться ожидание в течение 48 часов. Но преимущество в том, что база данных может обеспечить это с помощью:

from django.db import models
from django.db.models.functions import TruncDate


class Mining(models.Model):
    # …
    class Meta:
        constraints = [
            models.UniqueConstraint(
                TruncDate('created_at'), 'user_id', name='one_mining_per_day'
            )
        ]

A UniqueConstraint обычно применяется базой данных, и, следовательно, если проверка базы данных не отключена, вероятно, невозможно вставить два.

Вы можете использовать сигналы, но к сигналам есть много предостережений. В этом случае вы можете проверить, создаете ли вы объект с помощью:

@receiver(pre_save, sender=Mining)
def prevent_multiple_mining_per_day(sender, instance, **kwargs):
    if instance._state.adding:
        # …

Но это не сработает для массового создания.

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