当前位置: 首页 > article >正文

使用django的DRF业务逻辑应该放在序列化器类还是模型类

在 Django REST Framework (DRF) 中,序列化器和模型类有明确的职责划分。虽然序列化器在反序列化时负责接收、验证和转换数据,但模型类仍是整个系统的核心,承担更底层的职责。以下是详细解析:

一、序列化器 vs 模型类:职责对比

职责 序列化器 模型类

数据存储❌ 不直接操作数据库✅ 定义数据结构,直接操作数据
数据验证✅ 验证请求数据的格式和业务规则✅ 定义数据库层面的约束(如唯一性)
数据转换✅ 序列化(对象→JSON)和反序列化(JSON→对象)❌ 仅存储原始数据
业务逻辑⚠️ 处理与请求/响应相关的逻辑(如字段级校验)✅ 处理与数据密切相关的核心逻辑(如状态变更)
数据库关系✅ 描述关联关系的序列化方式✅ 定义外键、多对多等数据库关系
生命周期钩子❌ 仅限create()和update()✅ 支持save()、delete()前后的信号或pre_save等方法

二、为什么需要模型类?

数据持久化

模型类直接与数据库交互,负责数据的存储、查询、更新、删除(CRUD)。序列化器仅生成 Python 字典或模型实例,最终仍需调用模型的save()方法保存到数据库。

数据结构和约束

模型类通过字段(如CharField、ForeignKey)定义数据库表结构,并支持数据库级别的约束(如unique=True、db_index)。

核心业务逻辑

所有与数据直接相关的逻辑(如订单状态变更、用户积分计算)应放在模型类中,确保逻辑的复用性和一致性。

三、业务逻辑应该放在哪里?

1. 适合放在模型类中的逻辑

数据生命周期操作

例如:创建订单后自动发送邮件、删除用户时级联清理关联数据。

class Order(models.Model):status = models.CharField(max_length=20)def mark_as_paid(self):self.status = "paid"self.save()self.send_payment_confirmation_email()  # 调用发送邮件的逻辑

复杂计算或状态变更

例如:根据用户行为更新统计指标。

class UserProfile(models.Model):points = models.IntegerField()def add_points(self, amount):self.points += amountself.save()

数据库层面的验证

如通过模型的clean()方法补充校验:

class Article(models.Model):pub_date = models.DateField()def clean(self):if self.pub_date > timezone.now().date():raise ValidationError("发布日期不能晚于当前日期")

2. 适合放在序列化器中的逻辑

请求数据的校验和转换

例如:验证密码复杂度、动态修改输入数据。

class UserSerializer(serializers.ModelSerializer):def validate_password(self, value):if len(value) < 8:raise serializers.ValidationError("密码至少8位")return make_password(value)  # 对密码进行哈希处理

特定 API 的定制逻辑

例如:在创建对象时附加额外操作(如记录日志)。

class PostSerializer(serializers.ModelSerializer):def create(self, validated_data):post = super().create(validated_data)log_activity(user=post.author, action="create_post")  # 记录活动日志return post

动态字段控制

例如:根据用户权限返回不同字段。

class UserSerializer(serializers.ModelSerializer):class Meta:model = Userfields = ['id', 'username', 'email', 'is_staff']def to_representation(self, instance):data = super().to_representation(instance)if not self.context['request'].user.is_staff:del data['is_staff']  # 普通用户隐藏 is_staff 字段return data

3. 适合放在视图(View)中的逻辑

权限控制

例如:检查用户是否有权访问某个资源。

请求流程协调

例如:根据请求参数组合多个模型操作。

四、如何避免职责混乱?

遵循“Fat Models, Thin Serializers”原则
将核心业务逻辑下沉到模型类,序列化器仅处理与输入输出相关的逻辑。

不要重复校验

数据库约束(如unique=True)和模型校验(clean())是最后防线,序列化器的校验应专注于请求数据的预处理。

慎用序列化器的save()

如果保存对象时需要复杂操作,优先在模型类中定义自定义方法,而不是在序列化器中覆盖create()/update()。

五、示例场景

场景:用户注册

模型类

class User(models.Model):username = models.CharField(max_length=30, unique=True)email = models.EmailField(unique=True)password = models.CharField(max_length=128)def set_password(self, raw_password):self.password = make_password(raw_password)  # 密码哈希化

序列化器

class UserRegistrationSerializer(serializers.ModelSerializer):password = serializers.CharField(write_only=True)class Meta:model = Userfields = ['username', 'email', 'password']def validate_username(self, value):if 'admin' in value:raise ValidationError("用户名不能包含 admin")return valuedef create(self, validated_data):user = User(**validated_data)user.set_password(validated_data['password'])  # 调用模型的密码处理逻辑user.save()return user

视图

class UserRegistrationView(APIView):def post(self, request):serializer = UserRegistrationSerializer(data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data, status=201)return Response(serializer.errors, status=400)

六、总结

模型类:负责数据存储、核心业务逻辑和数据库层面的约束。

序列化器:负责数据校验、格式转换和与 API 交互的临时逻辑。

业务逻辑归属:

模型类:数据生命周期、复杂计算、跨接口复用逻辑。

序列化器:请求数据校验、输入输出定制化。

视图:权限控制、流程协调。

通过合理分层,可以避免代码冗余,提高可维护性和可扩展性。

相关文章:

使用django的DRF业务逻辑应该放在序列化器类还是模型类

在 Django REST Framework (DRF) 中&#xff0c;序列化器和模型类有明确的职责划分。虽然序列化器在反序列化时负责接收、验证和转换数据&#xff0c;但模型类仍是整个系统的核心&#xff0c;承担更底层的职责。以下是详细解析&#xff1a; 一、序列化器 vs 模型类&#xff1a…...

图片隐私清理工具

图片隐私清理助手&#xff1a;一键清除图片敏感信息的神器 在数字时代&#xff0c;我们每天都会拍摄和分享大量图片&#xff0c;但你是否注意过这些图片中可能暗藏隐私信息&#xff1f;相机的GPS定位、拍摄参数等EXIF数据&#xff0c;都可能在不经意间泄露你的隐私。今天介绍的…...

【UE5】摄像机晃动

目录 效果 步骤 一、游戏中晃动视角 二、Sequence中晃动视角 效果 步骤 一、游戏中晃动视角 1. 新建一个蓝图&#xff0c;父类选择“CameraShakeBase” 这里命名为“BP_MyCameraShake” 打开“BP_MyCameraShake”&#xff0c;根晃动模式这里设置为“Perlin噪点摄像机晃…...

类和对象—继承(1)

目录 1、继承1.1、继承的概念1.2、继承的语法 2、子类访问父类成员2.1、子类中访问父类的成员变量2.2、子类中访问父类的成员方法2.3、super 关键字 3、子类构造方法 1、继承 在 Java 中&#xff0c;类对现实中的实体进行描述&#xff0c;而类实例化的对象用来表示现实中的实体…...

CCF CSP 第33次(2024.03)(2_相似度计算_C++)(字符串中字母大小写转换+哈希集合)

CCF CSP 第33次&#xff08;2024.03&#xff09;&#xff08;2_相似度计算_C&#xff09; 题目背景&#xff1a;题目描述&#xff1a;输入格式&#xff1a;输出格式&#xff1a;样例1输入&#xff1a;样例1输出&#xff1a;样例1解释&#xff1a;样例2输入&#xff1a;样例2输出…...

试试智能体工作流,自动化搞定运维故障排查

APO 1.5.0版本全新推出的智能体工作流功能&#xff0c;让运维经验不再零散&#xff01;只需将日常的运维操作和故障排查经验转化为标准化流程&#xff0c;就能一键复用&#xff0c;效率翻倍&#xff0c;从此告别重复劳动&#xff0c;把时间留给更有价值的创新工作。更贴心的是&…...

Linux应用:线程基础

线程介绍 进程是程序在操作系统里的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的一个执行单元&#xff0c;是 CPU 调度和分派的基本单位。一个进程可以包含多个线程&#xff0c;这些线程共享进程的资源&#xff0c;如内存空间、文…...

ngx_conf_parse

配置文件 #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024; }http {#include mime.types;#default_type appli…...

要创建一个基于Spring Boot、Thymeleaf、MyBatis Plus和MySQL的简单表格增删改查(CRUD)项目

文章目录 要创建一个基于Spring Boot、Thymeleaf、MyBatis Plus和MySQL的简单表格增删改查&#xff08;CRUD&#xff09;项目1. 创建Spring Boot项目2.项目配置2.1 依赖yml配置数据库表配置 3.代码实现3.1 实体类3.2 数据访问层3.3 服务层3.4 控制层3.5 Thymeleaf模板 要创建一…...

解决Cubemx生产的 .ioc文件不能外部打开的方法

正常来说&#xff0c;cubemx生成的文件会有图标 但是当图标白色的时候&#xff0c;无法通过直接点击这个文件进入cubemx 1.首先检查java环境是不是装的JAVA8&#xff0c;如果是的话进行第二步操作&#xff1b; 2.重新安装一次cubemx&#xff0c;在安装的时候选择为我安装&…...

在 Linux(Ubuntu / CentOS 7)上快速搭建我的世界 MineCraft 服务器,并实现远程联机,详细教程

Linux 部署 MineCraft 服务器 详细教程&#xff08;丐版&#xff0c;无需云服务器&#xff09; 一、虚拟机 Ubuntu 部署二、下载 Minecraft 服务端三、安装 JRE 21四、安装 MCS manager 面板五、搭建服务器六、本地测试连接七、下载樱花&#xff0c;实现内网穿透&#xff0c;邀…...

Transformer | 一文了解:缩放、批量、多头、掩码、交叉注意力机制(Attention)

源自: AINLPer&#xff08;每日干货分享&#xff01;&#xff01;&#xff09; 编辑: ShuYini 校稿: ShuYini 时间: 2025-3-27 更多&#xff1a;>>>>专注大模型/AIGC、学术前沿的知识分享&#xff01; 引言 之前的文章&#xff1a;2万字长文&#xff01;一文了解…...

原型验证后客户推翻原有需求,如何止损

原型验证后客户推翻原有需求时止损的有效方法包括&#xff1a;迅速评估影响范围、立即开展沟通确认、调整项目计划和资源配置、更新变更管理流程、协商成本分担机制。其中&#xff0c;迅速评估影响范围是关键&#xff0c;项目团队必须立即明确此次变更的具体影响&#xff0c;包…...

六、小白学JAVA-类和对象

1、什么是类和对象 人类---类&#xff1a;走路、说话、学习 人---对象&#xff1a;具体到某个人&#xff0c;就是对象&#xff0c;走路、说话、学习&#xff0c;每个人都是独特的人。 public class Person {String name;public void walk() {System.out.println("我会走…...

CMLINK APN 手动设置

以下是针对 CMLINK 的 APN设置 的详细指南&#xff0c;基于常见配置需求&#xff1a; CMLINK APN 手动设置参数 参数项值说明名称CMLINK (自定义)任意命名&#xff08;如 CMLINK、CM Internet 等&#xff09;&#xff0c;建议使用ASCII字符&#xff0c;无特殊符号。APNcm.com …...

深入探索 Python 中的 asyncio:异步编程的利器

在当今的软件开发中&#xff0c;异步编程已经成为了提高程序性能和响应能力的重要手段之一。Python 作为一种广泛使用的编程语言&#xff0c;提供了强大的异步编程支持&#xff0c;而 asyncio 库则是其中的核心。本文将深入探讨 asyncio 的基本概念、使用方法以及一些高级特性&…...

STM32硬件IIC与OLED使用

OLED屏幕介绍 OLED即有机发光管(Organic Light-Emitting Diode,OLED)。OLED显示技术具有自发光、广视角、几乎无穷高的对比度、较低功耗、极高反应速度、可用于绕曲性面板、使用温度范围广、构造及制程简单等有点&#xff0c;被认为是下一代的平面显示屏新兴应用技术 OLED显示…...

基于Spring Boot的电动车智能充电服务平台的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

十、JavaScript对象

一、对象 创建对象的方法有三种&#xff1a;字面量、new、构造函数。 1.利用字面量创建对象 花括号{}里面包含了表达这个具体事物&#xff08;对象&#xff09;的属性和方法 // 1.利用对象字面量创建对象{}// var obj {}; // 创建了一个空的对象var obj {uname: black,ag…...

FFmpeg开发学习:音视频封装

1.基本流程 1.输入参数 输出文件路径 char *output 视频编码参数 AVCodecParameters *video_par 音频编码参数 AVCodecParameters *audio_par 数据包 AVPacket *packets[] 2.封装流程 &#xff08;1&#xff09;创建输出的上下文AVFormatContext指针 AVFormatContext *out_fm…...

hackmyvm-reversteg

arp-scan -l nmap -sS -v 192.168.222.45 在源码中可以看到 根据下面的提示可以猜测117db0148dc179a2c2245c5a30e63ab0是一个图像文件 将图片下载到本地 隐写术 在两张图片上使用strings,发现有一些可打印的字符串 strings 117db0148dc179a2c2245c5a30e63ab0.jpg base64解码…...

UE4学习笔记 FPS游戏制作17 让机器人持枪 销毁机器人时也销毁机器人的枪 让机器人射击

添加武器插槽 打开机器人的Idle动画&#xff0c;方便查看武器位置 在动画面板里打开骨骼树&#xff0c;找到右手的武器节点&#xff0c;右键添加一个插槽&#xff0c;重命名为RightWeapon&#xff0c;右键插槽&#xff0c;添加一个预览资产&#xff0c;选择Rifle&#xff0c;根…...

考研408-数据结构完整代码 线性表的链式存储结构 - 单链表

单链表操作详解&#xff08;C实现&#xff09; 目录 单链表尾插法创建单链表头插法创建删除指定节点按值查找按序号查找插入节点完整代码示例注意事项总结 尾插法创建 #include<bits/stdc.h> using namespace std;typedef struct LNode {int data;struct LNode* next;…...

蓝桥杯经典题解:班级活动分组问题的深度解析与优化实现

目录 一、问题背景与描述 二、问题分析与核心思路 2.1 问题本质&#xff1a;统计与配对优化 2.2 关键观察 2.3 数学建模 三、算法设计与实现步骤 3.1 算法步骤 3.2 代码实现&#xff08;Python&#xff09; 3.3 优化点分析 四、关键细节与常见误区 4.1 细节处理 4.…...

设计模式(创建型)-建造者模式

定义 建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。该模式允许通过多个简单的步骤逐步构建出一个复杂的对象&#xff0c;用户只需指定复杂对象…...

RIP和OSPF的区别

文章目录 RIP&#xff08;路由信息协议&#xff09;和 OSPF&#xff08;开放最短路径优先&#xff09;是两种常见的动态路由协议&#xff0c;它们的主要区别如下&#xff1a;1. 协议类型2. 更新方式3. 路由计算算法4. 最大跳数5. 管理距离&#xff08;AD&#xff09;6. 认证机制…...

Git 之配置ssh

1、打开 Git Bash 终端 2、设置用户名 git config --global user.name tom3、生成公钥 ssh-keygen -t rsa4、查看公钥 cat ~/.ssh/id_rsa.pub5、将查看到的公钥添加到不同Git平台 6、验证ssh远程连接git仓库 ssh -T gitgitee.com ssh -T gitcodeup.aliyun.com...

遍历数组时,如何获取数组每个元素索引号

在 JavaScript 中&#xff0c;有多种方法可以在遍历数组时获取每个元素的索引号&#xff0c;下面为你介绍几种常用的方法&#xff1a; 1. 使用 for 循环 const array [apple, banana, cherry]; for (let i 0; i < array.length; i) {console.log(索引 ${i} 的元素是: ${…...

黑马点评项目

遇到问题&#xff1a; 登录流程 session->JWT->SpringSession->tokenRedis &#xff08;不需要改进为SpringSession&#xff0c;token更广泛&#xff0c;移动端或者前后端分离都可以用&#xff09; SpringSession配置为redis模式后&#xff0c;redis相当于分布式se…...

如何防御TCP洪泛攻击

TCP洪泛攻击&#xff08;TCP Flood Attack&#xff09;是一种常见的分布式拒绝服务&#xff08;DDoS&#xff09;攻击手段&#xff0c;以下是其原理、攻击方式和危害的详细介绍&#xff1a; 定义与原理 TCP洪泛攻击利用了TCP协议的三次握手过程。在正常的TCP连接建立过程中&a…...