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

Django+DRF后端实现HTML5可编辑表格修改自动同步到数据库方案咨询

可编辑表格自动保存实现方案

一、后端接口调整

当前你仅实现了列表查询接口,需要补充单条数据的更新能力,以及调整序列化器的可写字段配置:

  1. 修改rest.py代码:
# 导入所需视图类
from rest_framework import generics, permissions

class MaterialSerializer(serializers.ModelSerializer):
    # 原有字段逻辑保持不变,仅调整Meta配置的只读字段
    # --- 原有代码省略 ---
    class Meta:
        model = Material
        fields = ['id', 'book', 'course', 'type','school', 'available', 'postgraduate', 'courseCode', 'courseId', 'year', 'month', 'term', 'quota',
                  'modified', 'discipline', 'remark', 'is_discard', 'discard_reason', 'retail_price_display']
        # 把不需要编辑的字段统一设为只读,需要编辑的字段(比如remark、is_discard等)不加入该列表即可
        read_only_fields = ['book', 'course', 'type','school', 'available', 'postgraduate', 'courseCode', 'courseId', 'year', 'month', 'term', 'quota',
                  'modified', 'discipline', 'retail_price_display']

# 原有列表视图升级为支持查询+新增
class MaterialList(generics.ListCreateAPIView):
    queryset = Material.objects.all()
    serializer_class = MaterialSerializer
    permission_classes = [permissions.IsAuthenticated]
    # 原有get_queryset逻辑保持不变
    # --- 原有代码省略 ---

# 新增单条数据详情视图,支持查询、修改、删除
class MaterialDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Material.objects.all()
    serializer_class = MaterialSerializer
    permission_classes = [permissions.IsAuthenticated]
  1. 补充路由配置,在你的urls.py中新增:
path('api/materials/<int:pk>/', MaterialDetail.as_view(), name='material-detail'),

二、前端逻辑实现

你使用的是BootstrapTable,推荐两种实现方案,按需选择即可:

方案1:使用BootstrapTable官方editable扩展(推荐)

  1. 先在页面引入editable相关依赖(BootstrapTable editable插件、x-editable库),然后修改表格列配置,给需要编辑的列添加data-editable="true"属性,比如要编辑备注列:
<th data-field="remark" data-editable="true">备注</th>
  1. 新增编辑提交逻辑,加防抖避免频繁请求:
// 防抖函数,用户停止输入300ms后再提交
const debounce = (func, wait = 300) => {
    let timeout;
    return (...args) => {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), wait);
    }
}

// 读取Django CSRF Token的辅助函数
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();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

// 表格初始化后监听编辑保存事件
$(function() {
    $('#bk-table').bootstrapTable({
        // 原有配置保持不变
        onEditableSave: debounce(function(field, row, oldValue, $el) {
            // 仅提交修改的字段
            const updateData = {[field]: row[field]};
            $.ajax({
                url: `/api/materials/${row.id}/`,
                type: 'PUT',
                headers: {'X-CSRFToken': getCookie('csrftoken')},
                contentType: 'application/json',
                data: JSON.stringify(updateData),
                success: () => console.log('保存成功'),
                error: () => {
                    // 保存失败回滚数据
                    $el.text(oldValue);
                    alert('保存失败,请重试');
                }
            })
        })
    })
})

方案2:原生contenteditable实现

如果不想引入额外插件,直接监听单元格失焦事件即可:

$('#bk-table').on('blur', 'td', debounce(function() {
    const $td = $(this);
    const rowId = $td.closest('tr').data('uniqueid');
    const row = $('#bk-table').bootstrapTable('getRowByUniqueId', rowId);
    const field = $td.data('field');
    const newValue = $td.text().trim();
    // 值未变化不提交
    if (newValue === row[field]) return;
    // 后续提交逻辑和方案1完全一致
}))

三、关联字段编辑特殊处理

如果你需要编辑嵌套的关联字段(比如discipline.name),需要在序列化器中重写update方法手动处理关联表更新:

class MaterialSerializer(serializers.ModelSerializer):
    # --- 原有代码省略 ---
    def update(self, instance, validated_data):
        # 提取关联的discipline修改数据
        discipline_data = validated_data.pop('discipline', None)
        if discipline_data:
            discipline = instance.course.courseInfo.discipline
            for k, v in discipline_data.items():
                setattr(discipline, k, v)
            discipline.save()
        # 剩余字段正常更新
        return super().update(instance, validated_data)

内容的提问来源于stack exchange,提问作者Django

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 00:42:02