Skip to content

Django - AJAX

AJAX (Asynchronous JavaScript and XML - 异步 JavaScript 和 XML,尽管现在 JSON 比 XML 更常用) 是一种用于创建更动态和响应更快的 Web 应用程序的技术。它允许在不重新加载整个页面的情况下更新网页的部分内容,从而带来更流畅的用户体验。

现代的 Django 应用通常不使用过时的库(如 django-dajax),而是使用标准的前端 JavaScript(通常是内置的 fetch API 或 Axios 等库)与返回数据(通常是 JSON 格式)或 HTML 片段的专用 Django 视图进行通信来实现 AJAX。

现代方法:JavaScript fetch + Django JsonResponse

Section titled “现代方法:JavaScript fetch + Django JsonResponse”

让我们用一个简单的例子来说明这一点:异步保存表单数据,而无需进行完整的页面刷新。

创建一个 Django 视图,用于处理传入的 POST 请求,处理表单数据,保存它(如果有效),并返回一个指示成功或失败的 JSON 响应。

from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt # 示例中为简单起见;生产环境需要为 AJAX 正确处理 CSRF!
from .forms import DreamrealForm # 假设你有一个 DreamrealForm
from .models import Dreamreal # 假设你有一个 Dreamreal 模型
import json
# @require_POST # 确保此视图只接受 POST 请求
# @csrf_exempt # 示例中为简单起见;生产环境需要为 AJAX 正确处理 CSRF!
def save_dreamreal_ajax(request):
if request.method == 'POST':
try:
# 如果内容类型是 application/json
if 'application/json' in request.content_type:
data = json.loads(request.body)
form = DreamrealForm(data)
# 如果内容类型是表单数据(例如,通过 JS 提交的标准 HTML 表单)
else:
form = DreamrealForm(request.POST)
if form.is_valid():
# 选项 1:如果 DreamrealForm 是标准的 forms.Form
dr = Dreamreal()
dr.website = form.cleaned_data.get('website')
dr.name = form.cleaned_data.get('name')
dr.phonenumber = form.cleaned_data.get('phonenumber')
# dr.email = form.cleaned_data.get('email') # 假设表单中包含 email 字段
dr.save()
# 选项 2:如果 DreamrealForm 是一个 ModelForm
# form.save()
return JsonResponse({'success': True, 'message': f'Entry {form.cleaned_data.get("name")} saved!'})
else:
# 以 JSON 格式返回表单错误
return JsonResponse({'success': False, 'errors': form.errors}, status=400)
except json.JSONDecodeError:
return JsonResponse({'success': False, 'message': 'Invalid JSON data'}, status=400)
except Exception as e:
return JsonResponse({'success': False, 'message': str(e)}, status=500)
# 如有必要,处理 GET 或其他方法,或返回方法不允许的响应
return JsonResponse({'success': False, 'message': 'Invalid request method'}, status=405)

关于 CSRF 的重要说明:

上面为了简化使用了 @csrf_exempt 装饰器。在实际应用中,你必须处理 AJAX 请求的 CSRF 保护。Django 的文档解释了如何在你的 JavaScript 请求中包含 CSRF token。参阅:https://docs.djangoproject.com/en/stable/howto/csrf/#ajax

将一个 URL 映射到这个新视图。

from django.urls import path
from . import views
urlpatterns = [
# ... 其他 URL
path('ajax/save_dreamreal/', views.save_dreamreal_ajax, name='save_dreamreal_ajax'),
# 一个用于渲染初始表单的视图(假设 dreamreal_form_view 存在)
path('dreamreal-form/', views.dreamreal_form_view, name='dreamreal_form'),
]

HTML 模板包含表单和处理提交的 JavaScript 代码。

<!DOCTYPE html>
<html>
<head>
<title>Dreamreal Form</title>
<style>
.errorlist { color: red; list-style-type: none; padding: 0; margin: 5px 0; }
.error { border: 1px solid red; }
</style>
</head>
<body>
<h2>Enter Dreamreal Details</h2>
<form id="my_form" method="POST">
{% csrf_token %} {# 对于标准 POST 很重要,但 JS 需要为 AJAX 处理它 #}
{{ form.as_p }} {# 假设 'form' 是由渲染此模板的视图传递过来的 #}
<button type="button" id="submit-button">Save via AJAX</button>
</form>
<div id="message-area"></div>
<script>
// 从 cookie 中获取 CSRF token 的函数(安全 AJAX POST 所需)
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// 这个 cookie 字符串是否以我们想要的名称开头?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
document.getElementById('submit-button').addEventListener('click', function() {
const form = document.getElementById('my_form');
const formData = new FormData(form);
const messageArea = document.getElementById('message-area');
const formUrl = '{% url "save_dreamreal_ajax" %}'; // 从 Django 模板标签获取 URL
// 清除之前的消息和错误
messageArea.innerHTML = '';
form.querySelectorAll('.errorlist').forEach(el => el.remove());
form.querySelectorAll('.error').forEach(el => el.classList.remove('error'));
fetch(formUrl, {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken, // 包含 CSRF token
'X-Requested-With': 'XMLHttpRequest' // 通常有助于服务器端检测
},
body: formData
})
.then(response => {
if (!response.ok) {
// 处理非 2xx 响应(例如验证错误的 400)
return response.json().then(data => {
throw { status: response.status, data: data };
});
}
return response.json(); // 解析成功响应的 JSON 体
})
.then(data => {
if (data.success) {
messageArea.style.color = 'green';
messageArea.textContent = data.message;
form.reset(); // 成功时清空表单
} else {
// 如果上述非 OK 响应抛出错误,可能不会到达这部分
// 但如果服务器返回 200 OK 且 success: false,则包含此部分以确保完整性
messageArea.style.color = 'red';
messageArea.textContent = data.message || 'An unknown error occurred.';
if (data.errors) {
displayFormErrors(data.errors);
}
}
})
.catch(errorInfo => {
console.error('Error:', errorInfo);
messageArea.style.color = 'red';
let errorMessage = 'An error occurred while submitting the form.';
if (errorInfo.status && errorInfo.data) {
errorMessage = `Error ${errorInfo.status}: ${errorInfo.data.message || 'Form submission failed.'}`;
if(errorInfo.data.errors) {
displayFormErrors(errorInfo.data.errors);
}
} else if (errorInfo.message) {
errorMessage = errorInfo.message;
}
messageArea.textContent = errorMessage;
});
});
function displayFormErrors(errors) {
const form = document.getElementById('my_form');
for (const fieldName in errors) {
const field = form.querySelector(`[name="${fieldName}"]`);
if (field) {
field.classList.add('error');
const errorList = document.createElement('ul');
errorList.classList.add('errorlist');
errors[fieldName].forEach(errorMsg => {
const listItem = document.createElement('li');
listItem.textContent = errorMsg;
errorList.appendChild(listItem);
});
// 在字段或其父段落之后插入错误
const parentElement = field.closest('p') || field.parentElement;
parentElement.parentNode.insertBefore(errorList, parentElement.nextSibling);
}
}
}
</script>
</body>
</html>
  • JavaScript 使用 fetch API 向 /ajax/save_dreamreal/ URL 发送 POST 请求。
  • 它在请求头中包含 CSRF token(从 cookie 中获取)。
  • 它使用 FormData 发送表单数据。
  • 它处理来自 Django 视图的 JsonResponse。
  • 如果成功(success: true),它会显示成功消息并清空表单。
  • 如果失败(success: false 或非 2xx HTTP 状态码),它会显示错误消息。如果返回了验证错误(errors 对象),它会尝试将其显示在相关的表单字段旁边。
  • 通过点击按钮触发表单提交,阻止了默认的整页表单提交。
  • 视图中的 @require_POST 装饰器确保只接受 POST 请求。
  • 视图使用 JsonResponse 返回结构化数据,JavaScript 可以轻松理解。
  • Axios: 一个流行的基于 Promise 的 JavaScript HTTP 客户端,提供了与 fetch 略有不同的 API,但实现相同的目标。
  • jQuery AJAX: 如果你的项目中已经在使用 jQuery,它的 $.ajax()、$.post()、$.get() 方法仍然被广泛使用且有效。
  • HTMX: 一个较新的库,允许你直接在 HTML 中使用特殊属性实现 AJAX 效果,减少了许多常见模式所需的自定义 JavaScript 量。它通常涉及从 Django 视图返回 HTML 片段而不是 JSON。
  • Django REST Framework (DRF): 对于构建更复杂的 API,DRF 是 Django 生态系统中的标准工具包。它简化了为 API 端点创建序列化器、视图、认证和权限的过程。

选择最适合你的需求复杂度和你对工具熟悉程度的方法。对于简单的 AJAX 交互,fetch 和 JsonResponse 通常就足够了。