Django - AJAX
Django 与异步请求 (AJAX)
Section titled “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”让我们用一个简单的例子来说明这一点:异步保存表单数据,而无需进行完整的页面刷新。
1. Django 视图 (views.py)
Section titled “1. Django 视图 (views.py)”创建一个 Django 视图,用于处理传入的 POST 请求,处理表单数据,保存它(如果有效),并返回一个指示成功或失败的 JSON 响应。
from django.http import JsonResponsefrom django.views.decorators.http import require_POSTfrom django.views.decorators.csrf import csrf_exempt # 示例中为简单起见;生产环境需要为 AJAX 正确处理 CSRF!from .forms import DreamrealForm # 假设你有一个 DreamrealFormfrom .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
2. URL 配置 (urls.py)
Section titled “2. URL 配置 (urls.py)”将一个 URL 映射到这个新视图。
from django.urls import pathfrom . 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'),]3. 模板 (dreamreal_form.html)
Section titled “3. 模板 (dreamreal_form.html)”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 使用
fetchAPI 向/ajax/save_dreamreal/URL 发送 POST 请求。 - 它在请求头中包含 CSRF token(从 cookie 中获取)。
- 它使用
FormData发送表单数据。 - 它处理来自 Django 视图的
JsonResponse。 - 如果成功(
success: true),它会显示成功消息并清空表单。 - 如果失败(
success: false或非 2xx HTTP 状态码),它会显示错误消息。如果返回了验证错误(errors对象),它会尝试将其显示在相关的表单字段旁边。 - 通过点击按钮触发表单提交,阻止了默认的整页表单提交。
- 视图中的
@require_POST装饰器确保只接受 POST 请求。 - 视图使用
JsonResponse返回结构化数据,JavaScript 可以轻松理解。
替代库/框架
Section titled “替代库/框架”- 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 通常就足够了。