Django получает несколько сообщений от браузера, Кэширует ли браузер POST-ы?
Во время разработки это происходит так: если я работаю над куском кода для получения POST-запроса, Django выдаст страницу с ошибкой, потому что код глючный. Затем я исправляю синтаксическую ошибку, обновляю, другая ошибка, исправляю эту, повторяю процесс, скажем, 8 раз
Но на 8-й раз код срабатывает, и тогда Django получает и сохраняет 8 записей Post, а не только последнюю!
Подобное поведение происходит в продакшене очень иногда. Я не уверен, что это браузер кэширует POST-ы, затем отправляет их все одновременно или Django?
У меня больше проблем с этим вопросом при работе с автономной функциональностью. Если POST не удался, я кэширую его, а затем повторно отправляю, когда он снова в сети. Но иногда неудачный POST возвращается из мертвых из другого измерения и отправляется в Django, как и Sync offline POST, так что теперь он дублируется.
Я написал код javascript, чтобы отключить кнопку отправки после нажатия. Я также проверяю наличие дубликатов записей (в течение 5 секунд) перед сохранением, но это работает, только если кто-то вручную отправляет два раза подряд, а не если он автоматически отправляет кучу POST одновременно. Оба эти способа являются "хаками", которые не устраняют основную причину.
Есть подсказки о том, как отладить или как попытаться решить/отладить проблему?
Это происходит на нескольких моих представлениях, но вот одно из них я сейчас пытаюсь исправить, ScrapEntryView - это представление, и оно является подклассом BaseFactCreateView:
from django.views import generic
from .apps import ScrapConfig as AppConfig
from .forms import ScrapEntryForm
from core.gui.apps import SESSION_FEEDBACK
from dist.machine.fact.views import BaseFactCreateView
from utils.time import ttb
from utils.validation import form_validation
from utils.web import HTTP_OK_NO_CONTENT
class BaseFactCreateView(generic.CreateView):
def form_invalid(self, form):
if form.is_sync_post:
# If it is a sync post, can't ask the user to correct it, accept it and log to SysEvent table
from core.base.models import SysEvent
SysEvent.objects.create(
name="Sync POST failed",
note=str(
f'Form errors: {form_validation.plain_text_errors(form)}, Request.POST{self.request.POST}'
),
)
return HTTP_OK_NO_CONTENT
return super().form_invalid(form)
def get_feedback(self):
raise NotImplementedError
def get_success_url(self):
return self.request.get_full_path_info()
def form_valid(self, form):
"""If the form is valid, save the associated model."""
response = super().form_valid(form) # will create self.object
self.request.session[SESSION_FEEDBACK] = self.get_feedback()
return response
class ScrapEntryView(BaseFactCreateView):
form_class = ScrapEntryForm
template_name = f'{AppConfig.name}/entry.html'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['site_area_id'] = self.kwargs['site_area_id']
kwargs['request'] = self.request
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['site_area_id'] = self.kwargs['site_area_id']
context['page_title'] = "Scrap Entry"
context[SESSION_FEEDBACK] = self.request.session.pop(SESSION_FEEDBACK, None)
return context
def get_feedback(self):
obj = self.object
return {
'type': 'success',
'heading': "Scrap Entry Saved",
'body': f"<b>{obj.weight_kg}</b>kg for machine <b>{obj.machine_setup.machine.name}</b><br>due to <b>{obj.scrap_reason.name}</b>",
'created_dtz': ttb.to_dstr(obj.created_utc, 'DATETIME_FORMAT'),
}