Подключаете примерно такую мидлварь
def _get_ending(n, endings):
n = n % 100
if n >= 11 and n <= 19:
return endings[2]
else:
n = n % 10
if n == 1:
return endings[0]
elif n > 1 and n < 5:
return endings[1]
else:
return endings[2]
def _get_message(limit):
if limit > 3660:
limit //= 3600
endings = [_('часа'), _('часов'), _('часов')]
elif limit > 60:
limit //= 60
endings = [_('минуты'), _('минут'), _('минут')]
else:
endings = [_('секунды'), _('секунд'), _('секунд')]
return _('Форма отправлялась менее {} {} назад').format(limit, _get_ending(limit, endings))
def post_limit_middleware(get_response):
limits = getattr(settings, 'REQUEST_LIMITS', {})
def middleware(request):
if request.method == 'POST':
match = resolve(request.path_info)
name = '{}:{}'.format(match.namespace, match.url_name) if match.namespace else match.url_name
limit = limits.get(name)
if limit is not None:
now = datetime.now()
user_id = request.user.username if request.user.is_authenticated else get_ip(request)
key = hashlib.md5(user_id.encode()).hexdigest()
cache_name = 'last-request.{0}.{1}'.format(name, key)
last_request = cache.get(cache_name, now - timedelta(days=1))
if (now - last_request) < timedelta(seconds=limit):
cache.set(cache_name, now)
if request.is_ajax():
response = JsonResponse([_get_message(limit)], safe=False)
response.status_code = 429
else:
response = render(request, '429.html', {'message': _get_message(limit)})
response.status_code = 429
return response
else:
cache.set(cache_name, now)
response = get_response(request)
return response
return middleware
Прописывается в settings.py настройку вида
REQUEST_LIMITS = {
'contact_form': 3600,
}
И живёте спокойно.