这里提到的所有 Javascript 解决方案以及 request.META.HTTP_REFERER 解决方案有时都有效,但两者都在相同的情况下中断(可能还有其他情况)。
我通常在创建或更改对象的表单下有一个取消按钮。如果用户提交表单一次并且服务器端验证失败,则再次向用户呈现表单,其中包含错误的数据。猜猜看,request.META.HTTP_REFERER 现在指向显示表单的 URL。您可以按取消一千次,并且永远不会回到最初的编辑/创建链接所在的位置。
我能想到的唯一可靠的解决方案有点复杂,但对我有用。如果有人知道一个更简单的解决方案,我很乐意听到它。 :-)
“诀窍”是将初始 HTTP_REFERER 传递到表单中并从那里使用它。因此,当表单被 POST 到时,它会传递正确的初始引用。
这是我的做法:
我为完成大部分工作的表单创建了一个 mixin 类:
from django import forms
from django.utils.http import url_has_allowed_host_and_scheme
class FormCancelLinkMixin(forms.Form):
""" Mixin class that provides a proper Cancel button link. """
cancel_link = forms.fields.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
"""
Override to pop 'request' from kwargs.
"""
self.request = kwargs.pop("request")
initial = kwargs.pop("initial", {})
# set initial value of 'cancel_link' to the referer
initial["cancel_link"] = self.request.META.get("HTTP_REFERER", "")
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
def get_cancel_link(self):
"""
Return correct URL for cancelling the form.
If the form has been submitted, the HTTP_REFERER in request.meta points to
the view that handles the form, not the view the user initially came from.
In this case, we use the value of the 'cancel_link' field.
Returns:
A safe URL to go back to, should the user cancel the form.
"""
if self.is_bound:
url = self.cleaned_data["cancel_link"]
# prevent open redirects
if url_has_allowed_host_and_scheme(url, self.request.get_host()):
return url
# fallback to current referer, then root URL
return self.request.META.get("HTTP_REFERER", "/")
用于编辑/创建对象的表单(通常是 ModelForm 子类)可能如下所示:
class SomeModelForm(FormCancelLinkMixin, forms.ModelForm):
""" Form for creating some model instance. """
class Meta:
model = ModelClass
# ...
视图必须将当前请求传递给表单。对于基于类的视图,您可以覆盖 get_form_kwargs():
class SomeModelCreateView(CreateView):
model = SomeModelClass
form_class = SomeModelForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
在显示表单的模板中:
<form method="post">
{% csrf token %}
{{ form }}
<input type="submit" value="Save">
<a href="{{ form.get_cancel_link }}">Cancel</a>
</form>