Как мне запретить пользователю создавать несколько объектов в течение 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:
# …
Но это не сработает для массового создания.