从传统Web到API驱动:使用Django REST Framework重构智能合同审查系统

从传统Web到API驱动:使用Django REST Framework重构智能合同审查系统

目录

1. 我们面临的三个核心问题

2. 场景实战:合同列表展示

2.1 传统 Django 模版系统实现

2.2 解耦后的 Django + DRF + 前端分离实现

3. 进阶技术实践

3.1 认证升级:集成 JWT 与自定义响应

3.2 复杂业务逻辑处理:APIView 的灵活性

4. 遇到的挑战与解决方案

挑战一:跨域资源共享 (CORS)

挑战二:文件上传与静态资源管理

5. 总结


在智能合同审查系统的开发演进过程中,我们经历了一次重要的架构转型:从传统的 Django 模版系统(MVT)迁移到了前后端分离的 API 驱动架构(Django REST Framework + Vue 3)。

这一转变并非盲目跟风,而是为了解决我们在初期开发中遇到的三个核心痛点。本文将结合具体的代码示例,详细对比这两种实现方式,并阐述重构带来的收益。

1. 我们面临的三个核心问题

在项目初期,我们使用了 Django 原生的模版系统。随着业务复杂度的增加,以下问题日益凸显:

耦合度高:前端页面逻辑(HTML/CSS/JS)与后端业务逻辑(Python View)紧密缠绕。修改一个按钮的样式,可能需要后端开发人员介入修改模版文件;后端修改数据结构,又极易打破前端的渲染逻辑。交互体验差:传统的 Web 应用是多页应用(MPA)。用户每次点击“下一页”、提交表单或进行筛选,浏览器都需要向服务器请求完整的 HTML 页面并重新加载。这种“白屏-加载-渲染”的循环无法提供类似原生应用的流畅体验。接口复用难:模版系统直接返回渲染好的 HTML 字符串。如果未来我们需要开发移动端 App 或微信小程序,现有的视图逻辑完全无法复用,必须重新开发一套返回 JSON 数据的 API。


2. 场景实战:合同列表展示

为了更直观地说明区别,我们以“获取并展示用户合同列表”这一高频场景为例,分别展示两种架构下的代码实现

2.1 传统 Django 模版系统实现

在传统模式下,后端 View 负责查询数据并将其“填”入 HTML 模版中,浏览器接收到的是最终的 HTML 页面

后端视图 (Views.py)

# 传统 Django View from django.shortcuts import render from .models import Contract def contract_list(request): # 1. 业务逻辑:查询当前用户的未删除合同 contracts = Contract.objects.filter( uploader=request.user, is_deleted=False ).order_by('-created_at') context = { 'contracts': contracts, 'username': request.user.username } # 2. 渲染:将数据与 HTML 模版混合,返回完整的 HTML 页面 return render(request, 'contract/contract_list.html', context)

前端模版 (contract_list.html)

<!-- Django Template Language (DTL) --> {% extends "base.html" %} {% block content %} <div> <h2>{{ username }} 的合同列表</h2> <table> <thead> <tr> <th>合同编号</th> <th>合同名称</th> <th>状态</th> <th>操作</th> </tr> </thead> <tbody> <!-- 逻辑与视图耦合:在 HTML 中写循环 --> {% for contract in contracts %} <tr> <td>{{ contract.contract_no }}</td> <td>{{ contract.name }}</td> <td>{{ contract.get_status_display }}</td> <td> <!-- 每次点击都会触发页面完全刷新 --> <a href="/contract/{{ contract.id }}/">查看</a> </td> </tr> {% empty %} <tr><td colspan="4">暂无合同</td></tr> {% endfor %} </tbody> </table> </div> {% endblock %}

局限性分析

强耦合:前端开发人员必须懂 Django 模版({% for contract in contracts %}、{{ }})语法,无法独立使用 Vue/React 等现代工具。体验卡顿:用户想要按“状态”筛选合同,必须点击按钮 -> 浏览器跳转 -> 服务器查询 -> 返回新 HTML -> 浏览器重绘。整个过程用户会感觉到明显的页面刷新接口复用难:如果明天要做 App,我们不能直接返回 HTML 给 App。所以不得不再写一套 API(通常是 JSON 格式),这就意味着同样的数据逻辑(获取文章列表)要写两遍,或者做复杂的判断。

2.2 解耦后的 Django + DRF + 前端分离实现

重构后,

后端仅充当数据提供者(Data Provider),通过 RESTful API 返回 JSON 数据;

前端作为独立应用(Vue 3),负责页面渲染和交互

第一步:定义序列化器 (Serializers.py)

后端首先定义“如何将数据库对象转换为 JSON 格式”

# backend/contract/serializers.py from rest_framework import serializers from .models import Contract class ContractListSerializer(serializers.ModelSerializer): # 自动处理关联字段,例如获取上传者的用户名 uploader_name = serializers.CharField(source='uploader.username', read_only=True) # 自动获取 choices 字段的显示文本(如 "reviewing" -> "审查中") status_display = serializers.CharField(source='get_status_display', read_only=True) class Meta: model = Contract # 明确指定返回给前端的字段,按需获取 fields = ['id', 'name', 'contract_no', 'status', 'status_display', 'uploader_name', 'updated_at']

第二步:编写 API 视图 (Views.py)

视图不再关注 HTML,只关注数据处理和权限

# backend/contract/views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import permissions from .models import Contract from .serializers import ContractListSerializer class ContractView(APIView): permission_classes = [permissions.IsAuthenticated] def get(self, request): """ 获取合同列表 API 返回格式:JSON """ # 1. 业务逻辑 queryset = Contract.objects.filter( uploader=request.user, is_deleted=False ).order_by('-created_at') # 2. 序列化:将 QuerySet 转换为 JSON 兼容的字典/列表 serializer = ContractListSerializer(queryset, many=True) # 3. 响应:返回纯数据 return Response({ 'code': 200, 'msg': '获取成功', 'data': serializer.data })

第三步:前端异步调用 (Vue 3)

前端完全独立,通过 AJAX (Axios) 请求数据,并在本地动态渲染

// frontend/src/views/contract/ContractHome.vue (Script部分) <script setup lang="ts"> import { ref, onMounted } from 'vue' import request from '@/utils/http-axios' // 定义纯粹的数据接口,不关心后端实现 interface ContractItem { id: number name: string contract_no: string status_display: string } const contractList = ref<ContractItem[]>([]) const loading = ref(false) // 异步获取数据,页面无需刷新 const fetchContracts = async () => { loading.value = true try { const res = await request.get('/api/contracts/') if (res.data.code === 200) { contractList.value = res.data.data } } finally { loading.value = false } } onMounted(() => { fetchContracts() }) </script> // Template 部分 (省略样式) <template> <div v-loading="loading"> <div v-for="item in contractList" :key="item.id"> <h3>{{ item.name }}</h3> <p>状态: {{ item.status_display }}</p> </div> </div> </template> 

重构后的收益

彻底解耦:后端开发人员只关注 API 文档和数据正确性;前端开发人员可以使用 Vue 生态的所有组件库(如 Element Plus),开发效率大幅提升极致体验:页面加载后,切换筛选条件或翻页时,JavaScript 只需请求微小的 JSON 数据并局部更新 DOM,用户几乎感觉不到延迟,实现了“单页应用 (SPA)”的丝滑体验一次开发,多端复用/api/contracts/ 接口返回的是标准的 JSON 数据Web端:Vue 解析 JSON 渲染表格移动端:iOS/Android App 解析同样的 JSON 渲染原生列表第三方集成:合作伙伴可以通过该 API 获取合同状态无需为新客户端重写任何后端代码


3. 进阶技术实践

除了基础的 CRUD 接口,DRF 在处理复杂业务场景时也展现出了强大的灵活性

3.1 认证升级:集成 JWT 与自定义响应

为了适应前后端分离的无状态特性,我们放弃了传统的 Session 认证,改用 JSON Web Token (JWT)。默认的 JWT 接口只返回 accessrefresh token,但在实际业务中,我们希望在登录成功后直接返回用户的基本信息(如角色、头像),以减少前端的二次请求

我们可以通过继承 TokenObtainPairSerializer 来实现自定义响应:

# backend/user/serializers.py from rest_framework_simplejwt.serializers import TokenObtainPairSerializer class MyTokenObtainPairSerializer(TokenObtainPairSerializer): def validate(self, attrs): data = super().validate(attrs) # 获取默认的 token 结果 # 注入额外的用户信息 data['user'] = { 'username': self.user.username, 'age': self.user.age, 'role': self.user.role, 'role_display': self.user.get_role_display() } return data

3.2 复杂业务逻辑处理:APIView 的灵活性

虽然 DRF 提供了 ModelViewSet 来快速生成标准接口,但对于“合同上传”这种涉及多步操作的复杂业务,我们更倾向于使用 APIView 来获得完全的控制权

例如,一个上传请求需要同时完成:文件存入 OSS -> 获取签名 URL -> OCR 解析 -> 存入数据库 -> 创建版本记录

# backend/contract/views.py class ContractView(APIView): parser_classes = [MultiPartParser, FormParser] # 支持文件上传 def post(self, request): file_obj = request.FILES.get('file') # 1. 上传至阿里云 OSS oss_object_name = upload_contract(file_obj) mock_file_url = get_signed_url(oss_object_name) # 2. 调用 OCR 解析合同文本 contract_text = parse_contract_from_url(mock_file_url) # 3. 序列化验证与数据保存 data = request.data.copy() serializer = ContractSerializer(data=data) if serializer.is_valid(): contract = serializer.save(uploader=request.user) # 4. 手动创建初始版本记录(原子性操作的一部分) ContractVersion.objects.create( contract=contract, version=1, file_url=mock_file_url, # ... ) return Response({'code': 200, 'msg': '上传成功'})

4. 遇到的挑战与解决方案

在重构过程中,我们也遇到了一些典型的“分离焦虑”,以下是我们的解决方案:

挑战一:跨域资源共享 (CORS)

前后端分离后,前端(如 localhost:5173)访问后端(localhost:8000)会遇到浏览器的同源策略限制

解决方案
安装 django-cors-headers 中间件,并在 settings.py 中配置 CORS_ALLOWED_ORIGINS,明确允许前端开发服务器的域名访问 API

挑战二:文件上传与静态资源管理

传统的 Django 静态文件管理(MEDIA_ROOT)在分离架构下不再适用,特别是涉及用户上传的敏感合同文件,直接暴露在文件系统中存在安全隐患且难以扩展

解决方案
引入阿里云 OSS 对象存储。后端不再存储物理文件,只负责生成上传凭证或接收文件流转发至 OSS,数据库仅存储文件的 Key 或 URL。前端通过后端返回的临时签名 URL 访问文件,既保证了安全性(链接有时效性),又减轻了应用服务器的带宽压力

5. 总结

通过引入 Django REST Framework,我们不仅解决了“耦合度高、体验差、复用难”这三大顽疾,更为系统的未来扩展打下了坚实基础。现在,我们的智能合同审查系统拥有了清晰的边界:后端是稳定高效的数据与逻辑中心,前端则是灵活多变的交互界面。

Read more

别再被忽悠!云计算+低代码,才是企业转型的真解药

别再被忽悠!云计算+低代码,才是企业转型的真解药

作为从业7年的IT互联网产品技术,我对接过数十家不同规模、不同行业的企业数字化转型项目,从中小型制造企业到大型政务单位,见过太多企业陷入“转型怪圈”:要么砸重金上云,却只做到“数据上云=转型完成”,核心业务流程依旧脱节;要么盲目跟风低代码,被厂商“3天开发一套系统”的宣传忽悠,最后只做出一堆无法复用、无法扩展的“无用表单”;更有甚者,将云计算与低代码割裂开来,各自为战,导致技术架构混乱、成本翻倍,转型半途而废。         今天不唱赞歌、不聊虚的,纯技术流视角拆解核心逻辑:企业转型的核心困境从来不是“缺技术”,而是“技术与业务脱节、效率与成本失衡、落地与迭代矛盾”;而云计算与低代码的深度协同,不是“锦上添花”,而是破解这些困境的唯一最优解——但前提是,你得看透二者的底层逻辑,避开行业里的那些坑,别再被厂商的虚假宣传带偏。 一、先破后立:企业转型的4大核心技术困境         在聊解决方案之前,我们先明确一个核心:企业数字化转型,本质是“用技术重构业务流程、用工具提升运营效率、

发送webhook到飞书机器人

发送webhook到飞书机器人

发送webhook到飞书机器人 参考链接 自定义机器人使用指南 创建自定义机器人 1. 邀请自定义机器人进群。 2. 3. 获取签名校验 在 安全设置 区域,选择 签名校验。 获取自定义机器人的 webhook 地址 机器人对应的 webhook 地址 格式如下: https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx 请妥善保存好此 webhook 地址,不要公布在 Gitlab、博客等可公开查阅的网站上,避免地址泄露后被恶意调用发送垃圾消息。 设置自定义机器人的头像、名称与描述,并点击 添加。 在 群机器人 界面点击 添加机器人。在 添加机器人 对话框,找到并点击 自定义机器人。

【论文阅读103】pinn-review-科学机器学习中的物理信息神经网络:现状与展望

【论文阅读103】pinn-review-科学机器学习中的物理信息神经网络:现状与展望

科学机器学习中的物理信息神经网络:现状与展望 作者:Salvatore Cuomo¹ · Vincenzo Schiano Di Cola² · Fabio Giampaolo¹ · Gianluigi Rozza³ · Maziar Raissi⁴ · Francesco Piccialli¹ 在线发表:2022年7月26日 摘要 物理信息神经网络(Physics-Informed Neural Networks,PINNs)是一类将模型方程(如偏微分方程,PDE)直接嵌入神经网络结构中的神经网络(NN)。目前,PINNs 已被广泛用于求解偏微分方程、分数阶方程、积分-微分方程以及随机偏微分方程。这一新兴方法作为一种多任务学习框架出现,在该框架中,神经网络不仅需要拟合观测数据,还需最小化 PDE 残差。 本文对物理信息神经网络相关文献进行了全面综述:研究的主要目标是阐明这类网络的特征、优势与局限性。同时,本文还涵盖了更广义的基于配点法(collocation-based)的物理约束神经网络研究,包括从最初的基础 PINN(

ManiSkill:打造高效机器人模拟环境的全栈指南

ManiSkill:打造高效机器人模拟环境的全栈指南 【免费下载链接】ManiSkill 项目地址: https://gitcode.com/GitHub_Trending/ma/ManiSkill 在机器人研发领域,选择合适的模拟环境直接决定开发效率。ManiSkill作为一款功能全面的机器人操作模拟平台,通过高度逼真的物理引擎和丰富的任务场景,帮助开发者快速验证算法有效性,显著降低真实世界测试成本。本文将系统介绍如何从零开始构建ManiSkill开发环境,掌握核心功能应用,并解决实践中可能遇到的技术难题。 1.环境准备:3步完成模拟系统部署 兼容性验证清单 在开始安装前,请确认您的系统满足以下条件: * Linux + NVIDIA GPU:完整支持CPU/GPU模拟及渲染功能 * Windows系统:支持CPU模拟和渲染,但GPU加速功能暂不可用 * MacOS系统:仅支持基础CPU模拟功能 核心安装命令 # 克隆项目仓库 git clone https://gitcode.com/GitHub_Trending/ma/ManiSkill cd ManiS