You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:17:39