Django CreateView未更新上下文数据



我有一个引导模式中的表单,我希望在提交后保持该模式的打开状态。我正在使用CreateView,并试图将一个额外的变量传递到前端的模板,在那里我可以检查是否设置了标志,但即使在提交后,标志也总是False。这是我所拥有的:

url.py

from django.urls import path
from .views import MescData
urlpatterns = [
path('mesc', MescData.as_view(), name='mesc')
]

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)
self.successful_submit = 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)

在Template中,我这样检查它:

{% if successful_submit %}
<h1>Flag is set</h1>
{% endif %}

是否有一种方法可以将CreateView中与表单无关的数据传递到模板,而无需更改url.py(即将变量数据添加到url路径(?

编辑:

我尝试在form_valid((和post((方法中打印self.successful_submit,它确实被更新为True,但在模板中它仍然被传递为False。

这是核心问题:"我有一个引导模式中的表单,我想在提交后保持该模式的打开状态">

简单的答案:使用Ajax。

我们现在确实有HTML作为一种越来越受欢迎的范例,但我对它还不够熟悉,无法讨论它。所以我将使用Ajax来展示一个解决方案。这个特殊的解决方案使用了一个通用的ajax模板,发布的结果是一个呈现的Django模板,您可以使用它来替换已经呈现的页面中的HTML。

此外,很少有人喜欢JavaScript,但这并不是避免使用它的充分理由。它在你运行的任何web应用程序中基本上都是强制性的。即使是网络上的HTML也只使用最少的JavaScript来实现其目标。

首先,编写Ajax视图。为此,我使用django-rest框架类,并提供了一个填写Calibration记录的示例。你可以使用任何你想要的形式;这正是我处理你想要保持开放的模态的方法。在这个视图中,如果POST成功,我将返回一个JSON响应。否则,我将返回一个渲染的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:'generalajax_modal.html,generalmodal.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 %}

现在,我已经将需要jQuery的JavaScript添加到模板中。我想我在上面做了我自己的jQuery插件。。。

$.fn.modalFormContainer = function(optionsObject) {
//call it on the modal div (the div that contains a modal-dialog, which contains modal-header, modal-body, etc
//  we expect there to be a form within that div, and that form should have an action url
//  when buttons that trigger the modal are clicked, the form is fetched from that action url and replaced.
// optionsObject has formAfterLoadFunction and ajaxDoneFunction
var options = $.extend({}, $.fn.modalFormContainer.defaults, optionsObject);
var $modalFormContainer = $(this);
// get the buttons that trigger this modal to open
//  add a click event so that the form is fetched whenever the buttons are clicked
//  if data-pk is an attribute on the button, apply that to the querystring of the
//      ajaxURL when fetching the form
var modalID = $modalFormContainer.prop("id");
var modalFormButtonSelector = "[data-target=#" + modalID + "][data-toggle=modal]";
function handleModalButtonClick(event) {
//does the button have an associated pk? if so add the pk to the querystring of the ajax url
//   this is wrapped in a form so that it gets replaced by the ajax response.
var $button = $(this);
if (!$button.hasClass("disabled") && !$button.prop("disabled")) { //only do it if the button is "enabled"
var $placeholder = $("<form><h1>loading...</h1></form>");
var $modalForm = $modalFormContainer.find("form");
var ajaxURL = $modalForm.prop("action");
$modalForm.replaceWith($placeholder);
var pk = $button.data().pk;
if (pk) {
if (ajaxURL.indexOf("?") > 0) {
ajaxURL += "&pk=" + pk;
} else {
ajaxURL += "?pk=" + pk;
}
}
//fetch the form and replace $modalFormContainer's contents with it
$.ajax({
type: "GET",
url: ajaxURL
}).done(function(response) {
// re-create the form from the response
$modalFormContainer.find(".modal-body").html(response);
$modalForm = $modalFormContainer.find("form"); //we would still need to find the form
options.formAfterLoadFunction($modalForm);
});
} else {
return false; //don't trigger the modal.
}
}
//using delegation here so that dynamically added buttons will still have the behavior.
// maybe use something more specific than '.main-panel' to help with performance?
$(".main-panel").on("click", modalFormButtonSelector, handleModalButtonClick);
$modalFormContainer.on("submit", "form", function(event) {
// Stop the browser from submitting the form
event.preventDefault();
var $modalForm = $(event.target);
var ajaxURL = $modalForm.prop("action");
$modalForm.find("[type=submit]").addClass("disabled").prop("disabled", true);
var formData = $modalForm.serialize();
var internal_options = {
url: ajaxURL,
type: "POST",
data: formData
};
// file upload forms have and enctype attribute
//    we should not process files to be converted into strings
if ($modalForm.attr("enctype") === "multipart/form-data") {
internal_options.processData = false;
internal_options.contentType = false;
internal_options.cache = false;
formData = new FormData($modalForm.get(0));
internal_options.data = formData;
}
$.ajax(internal_options).done(function(response) {
// blank out the form
$modalForm.find("input:visible, select:visible, textarea:visible").val("");
// remove errors on the form
$modalForm.find(".has-error").removeClass("has-error");
$modalForm.find("[id^=error]").remove();
$modalForm.find(".alert.alert-block.alert-danger").remove();
// hide the modal
$(".modal-header .close").click();
options.ajaxDoneFunction(response);
}).fail(function(data) {
// re-create the form from the response
$modalFormContainer.find(".modal-body").html(data.responseText);
options.formAfterLoadFunction($modalForm);
});
});
return this;
};
$.fn.modalFormContainer.defaults = {
formAfterLoadFunction: function($form) { return; },
ajaxDoneFunction: function(response) { return; }
};
$("#new-cal-modal").modalFormContainer({
formAfterLoadFunction: function($modalForm) {
$(".datetimeinput").datepicker('destroy');
$(".datetimeinput").datepicker();
},
ajaxDoneFunction: function(event) {
location.reload();
}
});

因此,在回顾这篇文章时,我意识到这个食谱比我欺骗自己相信的要复杂得多。对此我深表歉意。我希望您能够查看代码并了解正在发生的事情。这个食谱现在可以处理一些边缘情况,比如处理日期和文件上传,但你可能实际上并不需要它们。我应该提到的是,这个应用程序使用的是Bootstrap 3,所以在撰写本文时,它的样式没有更新到当前的Bootstrap 5。我应该补充一下,该应用程序的主要内容有一个类";"主面板";正如在这个不太通用的jQuery插件中使用的那样。

我担心我已经让你不知所措,无法维持你试图继续使用标准POST请求的立场。我想你可以用POST重新呈现模板,因为这将是你项目中的标准做法。你仍然可以在不使用查询字符串的情况下逃脱惩罚。

最新更新