CSRF 令牌丢失或不正确 - 在 Django 中使用自动完成灯
CSRF token missing or incorrect - using auto-complete light in Django
运行 到 CSRF_Token 我的 Django 应用程序中丢失或不正确,我不太明白问题是什么,因为我已经包含了 {% csrf_token %}我的模板呈现的任何可能的形式。我怀疑这可能与在表单中完成的 ajax 请求有关,以检索区域名称等,也许有人可以告诉我问题出在哪里。我正在使用 autocomplete-light 从我的数据库中检索一些数据,不知道这是否可以发挥作用。我试过在线搜索,但没有找到似乎适用于我的问题的解决方案。
Views.py
Class BreedAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return DogBreeds.objects.none()
qs = DogBreeds.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
class AdListTakeMyDog(generic.ListView):
model = Advertisement
context_object_name = 'ads'
template_name = 'core/advertisement_list_take.html'
def get_queryset(self):
queryset = Advertisement.objects.filter(is_offering_own_dog=True)
return queryset
class AdListGetMeADog(generic.ListView):
model = Advertisement
context_object_name = 'ads'
template_name = 'core/advertisement_list_get.html'
def get_queryset(self):
queryset = Advertisement.objects.filter(is_offering_own_dog=False)
return queryset
class NewAdTakeMyDog(CreateView):
model = Advertisement
form_class = NewAdTakeMyDogForm
success_url = reverse_lazy('view_ads_take_my_dog')
template_name = 'core/advertisement_form_take.html'
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.is_offering_own_dog = True
return super().form_valid(form)
class NewAdGetMeADog(CreateView):
model = Advertisement
form_class = NewAdGetMeADogForm
success_url = reverse_lazy('ad_changelist')
template_name = 'core/advertisement_form_get.html'
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.is_offering_own_dog = False
return super().form_valid(form)
advertisement_create - 模板
{% extends '_base.html' %}
{% load static %}
{% block content %}
<body onload="hide_area_field()">
{% block form_title %}
<h2>Ny annons</h2>
{% endblock form_title %}
{% if form.is_multipart %}
<form method="post" enctype="multipart/form-data" id="adForm" data-municipalities-url="{% url 'ajax_load_municipalities' %}" data-areas-url="{% url 'ajax_load_areas' %}" novalidate>
{% csrf_token %}
{% else %}
<form method="post" id="adForm" data-municipalities-url="{% url 'ajax_load_municipalities' %}" data-areas-url="{% url 'ajax_load_areas' %}" novalidate>
{% csrf_token %}
{% endif %}
<table>
{{ form.as_p }}
</table>
<button type="submit">Publicera annons</button>
{{ form.media }}
</form>
</body>
{% endblock content %}
{% block footer %}
{% comment %} Imports for managing Django Autocomplete Light in form {% endcomment %}
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/select2.css' %}" />
<script type="text/javascript" src="{% static 'autocomplete_light/jquery.init.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/autocomplete.init.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/select2.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/vendor/select2/dist/css/select2.css' %}" />
<script type="text/javascript" src="{% static 'autocomplete_light/vendor/select2/dist/js/select2.full.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/forward.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
{% comment %} Functions for handling area selectors {% endcomment %}
<script>
function hide_area_field() {
const area_selector = document.getElementById('id_area');
area_selector.style.display = 'none';
}
/* Get URL for requesting list of municipalities & Areas */
const baseURI = window.location.origin
const municipality_url = document.getElementById('adForm')['attributes']['data-municipalities-url']['value']
const area_url = document.getElementById('adForm')['attributes']['data-areas-url']['value']
/* Get relevant objects from DOM */
const province_selector = document.getElementById('id_province');
const municipality_selector = document.getElementById('id_municipality');
const area_selector = document.getElementById('id_area');
/* Update municipalities when Province have been chosen */
province_selector.addEventListener('change', (event) => {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
municipality_selector.innerHTML = xhr.response
}
}
};
xhr.open('GET', baseURI + municipality_url, true);
xhr.setRequestHeader('province', province_selector.value)
xhr.send();
});
/* Update Areas when Municipiality have been chosen */
municipality_selector.addEventListener('change', (event) => {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
if (xhr.response.length > 58) {
area_selector.style.display = ''
area_selector.innerHTML = xhr.response
} else {
area_selector.style.display = 'none'
}
}
}
};
xhr.open('GET', baseURI + area_url, true);
xhr.setRequestHeader('municipality', municipality_selector.value)
xhr.send();
});
</script>
{% endblock footer %}
forms.py
class NewAdTakeMyDogForm(forms.ModelForm):
breed = forms.ModelChoiceField(
queryset=DogBreeds.objects.all(),
widget=autocomplete.ModelSelect2(url='breed-autocomplete')
)
class Meta:
model = Advertisement
fields = ('province', 'municipality', 'area', 'title', 'description', 'days_per_week', 'size_offered', 'breed', 'image1', 'image2', 'image3')
def __init__(self, *args, **kwargs):
super(NewAdTakeMyDogForm, self).__init__(*args, **kwargs)
self.fields['municipality'].queryset = Municipality.objects.none()
self.fields['area'].queryset = Area.objects.none()
self.fields['area'].required = False
if 'province' in self.data:
try:
# Set municipality queryset
province_id = int(self.data.get('province'))
self.fields['municipality'].queryset = Municipality.objects.filter(province_id=province_id).order_by('name')
# Set area queryset
municipality_id = int(self.data.get('municipality'))
self.fields['area'].queryset = Area.objects.filter(municipality_id=municipality_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty Municipality/Area queryset
您的 AJAX 请求不包含 CSRF 令牌。于是Django的跨站请求伪造保护就开始了。
Django documentation
详细解释了如何在发送 AJAX POST 请求时获取 CSRF 令牌,以及如何将其包含在请求的 headers 中。
错误是我看错了模板,我有多个具有继承性的模板,并且把它们搞混了,所以虽然我在表单中显示了 CSRF 令牌,但它是在一个未使用的模板。经验教训 - 有一个 BASE 模板,您可以从该模板开始,并且只使用块来编写在基本模板之间更改的基本代码。
运行 到 CSRF_Token 我的 Django 应用程序中丢失或不正确,我不太明白问题是什么,因为我已经包含了 {% csrf_token %}我的模板呈现的任何可能的形式。我怀疑这可能与在表单中完成的 ajax 请求有关,以检索区域名称等,也许有人可以告诉我问题出在哪里。我正在使用 autocomplete-light 从我的数据库中检索一些数据,不知道这是否可以发挥作用。我试过在线搜索,但没有找到似乎适用于我的问题的解决方案。
Views.py
Class BreedAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return DogBreeds.objects.none()
qs = DogBreeds.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
class AdListTakeMyDog(generic.ListView):
model = Advertisement
context_object_name = 'ads'
template_name = 'core/advertisement_list_take.html'
def get_queryset(self):
queryset = Advertisement.objects.filter(is_offering_own_dog=True)
return queryset
class AdListGetMeADog(generic.ListView):
model = Advertisement
context_object_name = 'ads'
template_name = 'core/advertisement_list_get.html'
def get_queryset(self):
queryset = Advertisement.objects.filter(is_offering_own_dog=False)
return queryset
class NewAdTakeMyDog(CreateView):
model = Advertisement
form_class = NewAdTakeMyDogForm
success_url = reverse_lazy('view_ads_take_my_dog')
template_name = 'core/advertisement_form_take.html'
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.is_offering_own_dog = True
return super().form_valid(form)
class NewAdGetMeADog(CreateView):
model = Advertisement
form_class = NewAdGetMeADogForm
success_url = reverse_lazy('ad_changelist')
template_name = 'core/advertisement_form_get.html'
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.is_offering_own_dog = False
return super().form_valid(form)
advertisement_create - 模板
{% extends '_base.html' %}
{% load static %}
{% block content %}
<body onload="hide_area_field()">
{% block form_title %}
<h2>Ny annons</h2>
{% endblock form_title %}
{% if form.is_multipart %}
<form method="post" enctype="multipart/form-data" id="adForm" data-municipalities-url="{% url 'ajax_load_municipalities' %}" data-areas-url="{% url 'ajax_load_areas' %}" novalidate>
{% csrf_token %}
{% else %}
<form method="post" id="adForm" data-municipalities-url="{% url 'ajax_load_municipalities' %}" data-areas-url="{% url 'ajax_load_areas' %}" novalidate>
{% csrf_token %}
{% endif %}
<table>
{{ form.as_p }}
</table>
<button type="submit">Publicera annons</button>
{{ form.media }}
</form>
</body>
{% endblock content %}
{% block footer %}
{% comment %} Imports for managing Django Autocomplete Light in form {% endcomment %}
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/select2.css' %}" />
<script type="text/javascript" src="{% static 'autocomplete_light/jquery.init.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/autocomplete.init.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/select2.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'autocomplete_light/vendor/select2/dist/css/select2.css' %}" />
<script type="text/javascript" src="{% static 'autocomplete_light/vendor/select2/dist/js/select2.full.js' %}"></script>
<script type="text/javascript" src="{% static 'autocomplete_light/forward.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script>
{% comment %} Functions for handling area selectors {% endcomment %}
<script>
function hide_area_field() {
const area_selector = document.getElementById('id_area');
area_selector.style.display = 'none';
}
/* Get URL for requesting list of municipalities & Areas */
const baseURI = window.location.origin
const municipality_url = document.getElementById('adForm')['attributes']['data-municipalities-url']['value']
const area_url = document.getElementById('adForm')['attributes']['data-areas-url']['value']
/* Get relevant objects from DOM */
const province_selector = document.getElementById('id_province');
const municipality_selector = document.getElementById('id_municipality');
const area_selector = document.getElementById('id_area');
/* Update municipalities when Province have been chosen */
province_selector.addEventListener('change', (event) => {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
municipality_selector.innerHTML = xhr.response
}
}
};
xhr.open('GET', baseURI + municipality_url, true);
xhr.setRequestHeader('province', province_selector.value)
xhr.send();
});
/* Update Areas when Municipiality have been chosen */
municipality_selector.addEventListener('change', (event) => {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
if (xhr.response.length > 58) {
area_selector.style.display = ''
area_selector.innerHTML = xhr.response
} else {
area_selector.style.display = 'none'
}
}
}
};
xhr.open('GET', baseURI + area_url, true);
xhr.setRequestHeader('municipality', municipality_selector.value)
xhr.send();
});
</script>
{% endblock footer %}
forms.py
class NewAdTakeMyDogForm(forms.ModelForm):
breed = forms.ModelChoiceField(
queryset=DogBreeds.objects.all(),
widget=autocomplete.ModelSelect2(url='breed-autocomplete')
)
class Meta:
model = Advertisement
fields = ('province', 'municipality', 'area', 'title', 'description', 'days_per_week', 'size_offered', 'breed', 'image1', 'image2', 'image3')
def __init__(self, *args, **kwargs):
super(NewAdTakeMyDogForm, self).__init__(*args, **kwargs)
self.fields['municipality'].queryset = Municipality.objects.none()
self.fields['area'].queryset = Area.objects.none()
self.fields['area'].required = False
if 'province' in self.data:
try:
# Set municipality queryset
province_id = int(self.data.get('province'))
self.fields['municipality'].queryset = Municipality.objects.filter(province_id=province_id).order_by('name')
# Set area queryset
municipality_id = int(self.data.get('municipality'))
self.fields['area'].queryset = Area.objects.filter(municipality_id=municipality_id).order_by('name')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty Municipality/Area queryset
您的 AJAX 请求不包含 CSRF 令牌。于是Django的跨站请求伪造保护就开始了。
Django documentation 详细解释了如何在发送 AJAX POST 请求时获取 CSRF 令牌,以及如何将其包含在请求的 headers 中。
错误是我看错了模板,我有多个具有继承性的模板,并且把它们搞混了,所以虽然我在表单中显示了 CSRF 令牌,但它是在一个未使用的模板。经验教训 - 有一个 BASE 模板,您可以从该模板开始,并且只使用块来编写在基本模板之间更改的基本代码。