基于Django REST API与PouchDB的短时间离线同步实现咨询
嘿,结合你用Django DRF + PostgreSQL + jQuery的技术栈,要实现单客户端最多24小时离线使用的需求,用PouchDB确实是非常合适的选择。我之前做过类似的项目,给你梳理一套落地性强的实现方案:
基于PouchDB的24小时离线同步实现方案
一、核心架构思路
先理清楚整体逻辑:
- 前端用PouchDB做本地缓存数据库,离线时所有读写操作都先存在本地,完全不依赖后端
- 后端通过DRF扩展,实现和PouchDB兼容的同步接口(不用额外加数据库,直接对接你的PostgreSQL)
- 严格卡24小时离线时长:本地存储的操作只保留24小时,超时后就禁止离线操作,提示用户联网同步
二、后端改造步骤
1. 同步适配方案(推荐自定义DRF接口)
因为你用的是PostgreSQL,没必要额外加CouchDB中间层,自己写几个PouchDB需要的核心接口就行:
- 实现
_changes接口:给前端返回自上次同步以来的所有数据变更,格式要符合PouchDB的要求 - 实现
_bulk_docs接口:批量处理前端同步过来的离线操作(新增、修改、删除)
给你贴个简化的DRF视图示例:
from rest_framework.views import APIView from rest_framework.response import Response from .models import YourBusinessModel from .serializers import YourModelSerializer from datetime import datetime, timedelta class PouchChangesView(APIView): def get(self, request): # 前端会传上次同步的最后序列,用来过滤新数据 since_seq = request.query_params.get('since', 0) # 这里用id或者updated_at做序列都可以,看你业务 changes = YourBusinessModel.objects.filter(id__gt=since_seq) serializer = YourModelSerializer(changes, many=True) # 返回PouchDB能识别的格式 return Response({ "results": [{"seq": obj.id, "doc": serializer.data} for obj in changes], "last_seq": YourBusinessModel.objects.latest('id').id if changes.exists() else since_seq }) class PouchBulkDocsView(APIView): def post(self, request): # 批量处理前端同步的文档 docs = request.data.get('docs', []) for doc in docs: try: if '_id' in doc: # 更新已有记录 instance = YourBusinessModel.objects.get(id=doc['_id']) serializer = YourModelSerializer(instance, data=doc) else: # 创建新记录 serializer = YourModelSerializer(data=doc) if serializer.is_valid(): serializer.save() except YourBusinessModel.DoesNotExist: # 处理后端已删除但本地还存在的记录,直接跳过就行 continue return Response({"ok": True})
2. 离线时长校验逻辑
要控制24小时的离线上限,后端得有个接口来校验,同时前端本地也要做兜底判断(毕竟离线时连不上后端):
class OfflineValidityCheckView(APIView): def post(self, request): offline_start = request.data.get('offline_start') if not offline_start: return Response({"valid": False, "msg": "请提供离线起始时间"}) try: start_dt = datetime.fromisoformat(offline_start) if datetime.now() - start_dt > timedelta(hours=24): return Response({"valid": False, "msg": "离线时长已超24小时,请联网同步"}) return Response({"valid": True}) except ValueError: return Response({"valid": False, "msg": "时间格式错误"})
三、前端PouchDB集成与离线逻辑
1. 初始化PouchDB
先把本地数据库和后端同步接口连起来,注意带上Django的CSRF Token,不然会被拦截:
// 初始化本地PouchDB const localAppDB = new PouchDB('your_app_local_storage'); // 对接后端的同步接口 const remoteSyncDB = new PouchDB('/api/pouch-sync/', { adapter: 'http', ajax: { headers: { 'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val() } } });
2. 监听网络状态+控制离线时长
要记录离线开始时间,并且定期校验是否超时:
let offlineStartTime = null; // 联网时自动同步 window.addEventListener('online', () => { syncLocalToRemote(); }); // 离线时记录起始时间,同时校验是否已超时 window.addEventListener('offline', () => { offlineStartTime = new Date().toISOString(); checkOfflineValidity(); }); // 校验离线有效性,优先请求后端,离线时本地判断 function checkOfflineValidity() { if (!offlineStartTime) return; $.ajax({ url: '/api/offline-validity/', method: 'POST', data: JSON.stringify({offline_start: offlineStartTime}), contentType: 'application/json', success: (res) => { if (!res.valid) { alert(res.msg); disableOfflineOperations(); } }, error: () => { // 离线时用本地时间判断 const startDt = new Date(offlineStartTime); const now = new Date(); if (now - startDt > 24 * 60 * 60 * 1000) { alert('离线时长已超24小时,请尽快联网同步'); disableOfflineOperations(); } } }); } // 禁用所有离线操作按钮/表单 function disableOfflineOperations() { $('.offline-action-btn').prop('disabled', true); $('.offline-form').find('input, select, textarea').prop('disabled', true); }
3. 替换原有AJAX操作,改用PouchDB
把原来的GET/PUT请求换成读写本地PouchDB的操作,联网后再同步:
// 替代原AJAX GET:优先从本地读 function getLocalData() { return localAppDB.allDocs({include_docs: true}).then((result) => { return result.rows.map(row => row.doc); }); } // 替代原AJAX POST/PUT:先写本地 function saveToLocal(doc) { // 给文档加个时间戳,方便后续冲突处理 doc.updated_at = new Date().toISOString(); return localAppDB.put(doc); } // 同步本地数据到后端 function syncLocalToRemote() { localAppDB.sync(remoteSyncDB, { live: false, retry: false }).on('complete', () => { alert('数据同步完成!'); offlineStartTime = null; // 重新启用操作 $('.offline-action-btn').prop('disabled', false); $('.offline-form').find('input, select, textarea').prop('disabled', false); }).on('error', (err) => { alert(`同步失败:${err.message}`); }); }
四、几个关键坑要注意
- 冲突处理:离线时修改的数据和后端可能冲突,PouchDB会自动标记冲突,你得在前端加逻辑解决(比如提示用户选保留哪版,或者按时间戳自动覆盖)
- 数据清理:定期清理本地PouchDB里超过24小时的旧数据,别占用户存储空间
- 测试场景:一定要模拟各种极端情况,比如离线时多次改同一数据、超时后强行操作、联网后同步失败等
内容的提问来源于stack exchange,提问作者Ashwin Bande
相关产品推荐
相关产品推荐

