Django CreateView передает дополнительные данные контекста
У меня есть форма в модале bootstrap, и я хочу, чтобы этот модал оставался открытым после отправки. Я использую CreateView и пытаюсь передать дополнительную переменную в шаблон во front-end, где я мог бы проверить, установлен ли флаг или нет, но флаг всегда False даже после отправки. Вот что у меня есть:
views.py
from django.urls import reverse
from django.views.generic.edit import CreateView
from .forms import MescForm
from .models import Mesc
class MescData(CreateView):
model = Mesc
form_class = MescForm
template_name = 'Materials/mesc_data.html'
successful_submit = False # Flag to keep the add entry modal open
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['successful_submit'] = self.successful_submit
return context
def get_success_url(self):
return reverse('mesc')
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
form.was_satisfied = True
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form, **kwargs):
# self.successful_submit = True
return super(MescData, self).form_valid(form, **kwargs)
А в Шаблоне я проверяю его следующим образом:
{% if success %}
<h1>Flag is set</h1>
{% endif %}
Есть ли способ передать данные, не относящиеся к форме в CreateView, в шаблон без необходимости изменять url.py (т.е. добавлять переменные данные в путь к url)?
Вот основная проблема: "У меня есть форма в модале bootstrap, и я хочу, чтобы этот модал оставался открытым после отправки"
Простой ответ: Используйте Ajax.
Сейчас у нас есть HTML over the wire как парадигма, набирающая популярность, но я недостаточно знаком с ней, чтобы обсуждать ее. Поэтому я буду использовать Ajax, чтобы показать решение. Это конкретное решение использует общий шаблон ajax, и результатом поста является отрисованный шаблон Django, который вы можете использовать для простой замены HTML в вашей уже отрисованной странице
Кроме того, мало кому нравится JavaScript, но это не является достаточно веской причиной, чтобы не использовать его. Он практически обязателен в любом веб-приложении, которое вы запускаете. Даже HTML по проводам использует минимальное количество JavaScript для достижения своих целей.
Сначала напишите ваше представление Ajax. Я использую для этого классы django-rest-framework и привожу пример заполнения записи Calibration. Вы можете использовать любую форму, какую захотите; это просто мой рецепт для обработки модалов, которые вы хотите держать открытыми. В этом представлении я возвращаю ответ в формате JSON, если POST был успешным. В противном случае я возвращаю отрисованный шаблон Django.
from rest_framework.generics import (CreateAPIView, RetrieveAPIView,
UpdateAPIView, get_object_or_404)
from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer
from rest_framework.response import Response
class CalibrationCreateAjaxView(CreateAPIView, UpdateAPIView, RetrieveAPIView):
renderer_classes = (TemplateHTMLRenderer,)
template_name = "documents/form/cal.html"
def post(self, request, *args, **kwargs):
context = self.get_context(request)
calibration_form = context['calibration_form']
if calibration_form.is_valid():
calibration_form.save()
request.accepted_renderer = JSONRenderer()
return Response(status=201)
return Response(context, status=400)
def get(self, request, *args, **kwargs):
return Response(self.get_context(request))
@staticmethod
def get_context(request):
pk = request.GET.get("pk")
calibration_entry = get_object_or_404(CalibrationEntry, pk=pk) if pk else None
return {
'calibration_form': CalibrationFormAjax(request.POST or None, instance=calibration_entry)
}
У меня тоже есть свой шаблон представления. Он использует преимущества request.is_ajax, который устаревает. Вам нужно будет добавить некоторое промежуточное ПО, чтобы продолжать использовать его. Вот мое промежуточное ПО. Добавьте его также в ваш файл настроек.
class IsAjaxMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
"""
request.is_ajax is being removed in Django 4
Since we depend on this in our templates, we are adding this attribute to request
Please review:
https://docs.djangoproject.com/en/4.0/releases/3.1/#id2
"""
def __call__(self, request):
request.is_ajax = request.headers.get('x-requested-with') == 'XMLHttpRequest'
return self.get_response(request)
general/ajax_modal.html
<!-- {% block modal_id %}{% endblock %}{% block modal_title %}{% endblock %} -->
{% block modal_body %}
{% endblock modal_body %}
general/modal.html
<div class="modal fade" id="{% block modal_id %}{{ modal_id }}{% endblock modal_id %}" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">Close</span>
</button>
<h4 class="modal-title">
{% block modal_title %}{{ modal_title }}{% endblock modal_title %}
</h4>
</div>
<div class="modal-body">
{% block modal_body %}
{% endblock modal_body %}
</div>
</div>
</div>
</div>
Хотя мы используем Crispy Forms, вы можете обойтись без его использования. У меня также есть общая библиотека templatetag, которая отображает любые ошибки на форме. Вы можете написать свою собственную.
documents/form/cal.html
{% extends request.is_ajax|yesno:'general\ajax_modal.html,general\modal.html' %}
{% load crispy_forms_tags general %}
{% block modal_id %}new-cal-modal{% endblock modal_id %}
{% block modal_title %}Enter Calibration Information{% endblock modal_title %}
{% block modal_body %}
<div id="new-cal-form-container">
<form action="{% url 'calibration-create' %}" method="post" id="new-cal-modal-form" autocomplete="off" novalidate>
{% if request.is_ajax %}
{% crispy calibration_form %}
{% form_errors calibration_form %}
{% endif %}
<button type="submit" name="Submit" value="Save" class="btn btn-success button" id="submit">save</button>
</form>
</div>
{% endblock modal_body %}
Теперь, когда представление Ajax настроено, я возвращаюсь на главную страницу, которая будет отображать модальный диалог, когда пользователь нажмет на кнопку. У меня есть блок под названием "extraContent", в который я включаю шаблон модальной формы.
{% block extraContent %}
{% include 'documents/form/cal.html' %}
{% endblock extraContent %}
А теперь JavaScript, требующий jQuery, который я добавил в шаблон. Полагаю, я сделал свой собственный плагин jQuery поверх этого...
Пересмотрев его, я понял, что этот рецепт гораздо сложнее, чем я сам себя обманывал. Я искренне извиняюсь за это. Я надеюсь, что вы сможете просмотреть код и получить представление о том, что происходит. Есть некоторые крайние случаи, такие как работа с датами и загрузка файлов, которые этот рецепт обрабатывает прямо сейчас, но, возможно, они вам не понадобятся. Я должен упомянуть, что приложение, из которого это взято, использует Bootstrap 3, поэтому его стилизация не обновлена до текущего Bootstrap 5 на момент написания этой статьи. Следует добавить, что основное содержимое приложения имеет класс "main-panel", используемый в этом не совсем обычном jQuery-плагине.
Я беспокоюсь, что я переборщил и заставил вас сохранить вашу позицию, пытаясь продолжать использовать стандартный POST-запрос. Думаю, вы можете просто перерисовать шаблон с помощью POST, поскольку это будет стандартной практикой в вашем проекте. Вы все еще можете обойтись без использования строки запроса таким образом.