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">&times;</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, поскольку это будет стандартной практикой в вашем проекте. Вы все еще можете обойтись без использования строки запроса таким образом.

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