如何在Django默认管理页面实现博客自动保存功能?
Django Admin 博客自动保存最优实现方案
核心思路
在现有博客模型中添加草稿存储字段,结合自定义Admin视图+前端输入触发的AJAX请求,实现自动保存草稿功能,同时适配新文章和已有文章场景。
步骤1:扩展博客模型
新增草稿内容和上次保存时间字段,专门存储自动保存的内容:
# models.py from django.db import models from django.utils import timezone class BlogPost(models.Model): title = models.CharField(max_length=200) content = models.TextField() # 自动保存草稿字段 draft_content = models.TextField(blank=True, null=True) last_saved = models.DateTimeField(blank=True, null=True) def save(self, *args, **kwargs): # 正式发布时同步草稿内容到主内容(可选) if self.draft_content and not self.content: self.content = self.draft_content super().save(*args, **kwargs)
步骤2:自定义Admin草稿保存视图
在Admin中添加处理草稿保存的视图,自动处理权限验证和CSRF:
# admin.py from django.contrib import admin from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from django.views import View from .models import BlogPost from django.utils import timezone from django.urls import path class BlogPostDraftSaveView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def post(self, request, pk=None): # 权限验证由admin_view自动处理 if pk: post = BlogPost.objects.get(pk=pk) else: # 新文章自动创建草稿实例 post = BlogPost.objects.create(title="未命名草稿") post.draft_content = request.POST.get('content', '') post.last_saved = timezone.now() post.save() return JsonResponse({ 'status': 'success', 'last_saved': post.last_saved.strftime('%Y-%m-%d %H:%M:%S'), 'post_id': post.id }) class BlogPostAdmin(admin.ModelAdmin): # 指定自定义模板注入JS change_form_template = 'admin/blog_post_change_form.html' def get_urls(self): urls = super().get_urls() custom_urls = [ path('<int:pk>/save-draft/', self.admin_site.admin_view(BlogPostDraftSaveView.as_view()), name='blog-post-save-draft'), path('save-draft/', self.admin_site.admin_view(BlogPostDraftSaveView.as_view()), name='blog-post-save-draft-new'), ] return custom_urls + urls admin.site.register(BlogPost, BlogPostAdmin)
步骤3:前端实现自动保存逻辑
创建自定义Admin模板,注入JavaScript实现输入停止后自动保存:
<!-- templates/admin/blog_post_change_form.html --> {% extends "admin/change_form.html" %} {% block extrahead %} {{ block.super }} <script> const autoSaveDelay = 5000; // 输入停止后5秒触发保存 let postId = '{{ object_id }}'; let saveTimeout; function saveDraft() { const contentField = document.getElementById('id_content'); if (!contentField) return; const formData = new FormData(); formData.append('content', contentField.value); formData.append('csrfmiddlewaretoken', document.querySelector('[name=csrfmiddlewaretoken]').value); const url = postId ? `{% url 'admin:blog-post-save-draft' object_id %}` : `{% url 'admin:blog-post-save-draft-new' %}`; fetch(url, { method: 'POST', body: formData }) .then(res => res.json()) .then(data => { if (data.status === 'success') { // 更新自动保存提示 let notice = document.getElementById('auto-save-notice'); if (!notice) { notice = document.createElement('div'); notice.id = 'auto-save-notice'; notice.style = 'margin:10px 0; padding:8px; background:#e6f7ff; border:1px solid #91d5ff; border-radius:4px;'; contentField.parentElement.appendChild(notice); } notice.textContent = `自动保存成功:${data.last_saved}`; // 新文章更新ID,避免丢失草稿 if (!postId) { postId = data.post_id; window.history.replaceState({}, '', window.location.pathname.replace('/add/', `/change/${data.post_id}/`)); } } }) .catch(err => console.error('自动保存失败', err)); } // 页面加载后绑定事件 document.addEventListener('DOMContentLoaded', () => { const contentField = document.getElementById('id_content'); if (contentField) { // 输入停止后触发保存 contentField.addEventListener('input', () => { clearTimeout(saveTimeout); saveTimeout = setTimeout(saveDraft, autoSaveDelay); }); // 页面加载时自动保存一次(可选) setTimeout(saveDraft, 1000); } }); </script> {% endblock %}
针对你之前Ajax失败的关键修复
- CSRF令牌处理:通过FormData携带
csrfmiddlewaretoken,避免请求被Django拦截 - 新文章实例创建:针对未发布的新文章,自动创建草稿实例并返回ID,解决无实例可保存的问题
- 权限验证:使用
admin_site.admin_view包装视图,确保只有有权限的用户能访问 - 内容获取准确性:直接通过Admin默认字段ID(
id_content)获取内容,避免选择器错误
内容的提问来源于stack exchange,提问作者Franklyn Etu
相关产品推荐
相关产品推荐

