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

sqlalchemy.orm中validates对两个字段进行联合校验

版本

sqlalchemy==1.4.37

需求说明

有个场景,需要在orm中对两个字段进行联合校验,当 col1 ='xxx’时,对 col2的长度进行检查,超过限制(500)时,进行截断。

网上找了很久,没找到类似的实现,自己摸索出来了一套方法;

解决

在 validates 装饰器中,它是在设置字段值之前被调用的,validates 包装的函数校验完成后通过return赋值给字段

validates 的执行顺序 看起来是和 字段 传入ORM模型的顺序 一样。
如 下面案例 中的 model_instance 中,如果 col1 在 col2 之前,就会先校验和赋值 col1 ,反之,则先校验和赋值 col12

方案1

保证 model_instance 中 字段 col1 在 col2 之前,这样会先校验和赋值 col1 ,在校验 col2 时,self.col1 就不会为空,能正常进行校验。

如果先赋值col2,在 validate_col2 中,会self.col1会为None,导致校验失败

from sqlalchemy.orm import validates
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class MyModel(Base):__tablename__ = 'my_model'id = Column(Integer, primary_key=True)col1 = Column(String(50))col2 = Column(String(500))  # 假设col2的最大长度是500个字符@validates('col2')def validate_col2(self, key, value):# 检查col1的值是否为'xxx'if self.col1 == 'xxx':# 如果col1是'xxx',则校验col2的长度if len(value) > 500:value = value[:500]# 如果col1不是'xxx',可以选择不做任何操作或者添加其他逻辑return value # 示例使用1
# 先赋值 col1 再赋值 col2
model_instance = MyModel()
model_instance.col1 = 'xxx'  # 假设这是触发条件的值
model_instance.col2 = 'a' * 501  # 这将触发长度校验# 示例使用2
datas = {'col1'= 'xxx', 'col2': 'a' * 501 }
# 先pop删除,再添加,就不管前面datas是怎么来的,可以保证 datas 中 col2会比col1后遍历到
_col2_v = datas.pop('col2')
datas['col2'] = _col2_v  
model_instance = MyModel(**datas )try:# 假设这是保存模型到数据库的代码# session.add(model_instance)# session.commit()pass
except ValueError as e:print(e)

方案2

实例化orm模型后,再调用一遍 validate_col2 ,校验并赋值给col2

from sqlalchemy.orm import validates
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class MyModel(Base):__tablename__ = 'my_model'id = Column(Integer, primary_key=True)col1 = Column(String(50))col2 = Column(String(500))  # 假设col2的最大长度是500个字符@validates('col2')def validate_col2(self, key, value):# 检查col1的值是否为'xxx'if self.col1 == 'xxx':# 如果col1是'xxx',则校验col2的长度if len(value) > 500:value = value[:500]# 如果col1不是'xxx',可以选择不做任何操作或者添加其他逻辑return value # 示例使用1
# 先赋值 col1 再赋值 col2
model_instance = MyModel()
model_instance.col1 = 'xxx'  # 假设这是触发条件的值
model_instance.col2 = 'a' * 501  # 这将触发长度校验# 示例使用2
datas = {'col1'= 'xxx', 'col2': 'a' * 501 }model_instance = MyModel(**datas )
# 上面实例化会自动调用所有的 `validates` 函数,下面再调用一遍`validate_col2`, 
# 并且要用 `model_instance.col2` 接收返回值,不然 `model_instance.col2` 的值不会改变。
model_instance.col2 = model_instance.validate_col2('col2', datas['col2'])try:# 假设这是保存模型到数据库的代码# session.add(model_instance)# session.commit()pass
except ValueError as e:print(e)

方案3

无法保证 model_instance 中 字段 col1 在 col2 之前的顺序,采用 临时变量 __col1,存储 col1 的值,并对 col2 进行二次校验赋值

validate_col1 函数中,校验 col1,先把 value 值(就是没校验前的col1的值)赋给 self.__col1,然后再调用 validate_col1_and_col2 进行联合校验,最后通过 return把value赋值给 self.col1

在整个过程中,validate_col1_and_col2 会被调用3次

  • 校验 col2 时,validate_col2 会调用一次
  • 校验 col1 时,self.col2 = self.validate_col1_and_col2(key='col2', value=self.col2) 这一行会调用两次:
    1. 一次是 self.validate_col1_and_col2 执行;
    2. 另一次是 1 执行完后对 self.col2 赋值,会调用一次 validate_col2 ,进而再调用一次
from sqlalchemy.orm import validates
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_basedef getStrLenAndTruncate(ss: str, max_length=500):"""获取字符串长度,超过部分截断:param ss::param max_length::return:"""slen = len(ss.encode('utf-8'))# 如果编码后的字符串长度小于或等于500字节,则不需要截断if slen <= max_length:return ss# 截断到500字节的长度,注意这里直接截断可能会导致字符不完整# 因此需要找到一个合适的截断点,确保截断后的字符串是完整的utf-8字符truncated_encoded = b''current_length = 0for char in ss:char_encoded = char.encode('utf-8')if current_length + len(char_encoded) <= max_length:truncated_encoded += char_encodedcurrent_length += len(char_encoded)else:breaktruncated_str = truncated_encoded.decode('utf-8', errors='ignore')print(f'原字符串编码后长度为{slen}, 超过限制{max_length}, 需进行截断\n原字符串={ss}, \n截断后字符串:{truncated_str}')return truncated_strBase = declarative_base()class MyModel(Base):__tablename__ = 'my_model'__col1 = ''id = Column(Integer, primary_key=True)col1 = Column(String(50))col2 = Column(String(500))  # 假设col2的最大长度是500个字符@validates('col1')def validate_col1(self, key, value):self.__col1 = valueself.col2 = self.validate_col1_and_col2(key='col2', value=self.col2)return value@validates('col2')def validate_col2(self, key, value):value = validate_col1_and_col2(key, value)return value def validate_col1_and_col2(self, key, value):# 检查col1的值是否为'xxx'if self.__col1== 'xxx':if not value:value = ''elif len(value) * 3 <= 500:# 存储到 oracle,中文占3个字符passelse:print('需检查 col2 长度')value = getStrLenAndTruncate(value)return value# 示例使用
datas = {'col1'= 'xxx', 'col2': 'a' * 501 }
model_instance = MyModel(**datas )try:# 假设这是保存模型到数据库的代码# session.add(model_instance)# session.commit()pass
except ValueError as e:print(e)

说明

为啥不省略下面这个 validate_col2 这个校验代码:

    @validates('col2')def validate_col2(self, key, value):value = validate_col1_and_col2(key, value)return value 

因为 这个方案中,col1 、col2进入 orm模型的顺序不一定,如果省略了validate_col2 ,当col1比col2先进入模型,那么在 validate_col1 调用 self.validate_col1_and_col2(key='col2', value=self.col2) 时,self.col2其实等于None,此时对col2校验是没有意义的。等到 col2 进入 orm模型,又缺少对它进行校验的函数。

注意:

不能在 某个字段的校验函数中对其进行赋值操作,不然会陷入递归循环,因为赋值操作会调用校验函数;

如下面的调用会陷入递归死循环,因为 self.col1 = value 这行代码对 self.col1 进行了赋值,会自动再次调用validate_col1校验函数,就会在这一行陷入递归死循环而报错。


class MyModel(Base):@validates('col1')def validate_col1(self, key, value):self.__col1 = valueself.col1 = valueself.col2 = self.validate_col1_and_col2(key='col2', value=self.col2)return value

其他方案

不走orm模型,直接写校验代码和原生sql处理。

最后

这就是我尝试出来的 在 sqlalchemy.orm中validates对两个字段进行联合校验的方法,总感觉不太完美,不知道有没有大佬知道更好的方案,欢迎分享

相关文章:

sqlalchemy.orm中validates对两个字段进行联合校验

版本 sqlalchemy1.4.37 需求说明 有个场景&#xff0c;需要在orm中对两个字段进行联合校验&#xff0c;当 col1 xxx’时&#xff0c;对 col2的长度进行检查&#xff0c;超过限制&#xff08;500&#xff09;时&#xff0c;进行截断。 网上找了很久&#xff0c;没找到类似的…...

【ROS2】高级:解锁 Fast DDS 中间件的潜力 [社区贡献]

目标&#xff1a;本教程将展示如何在 ROS 2 中使用 Fast DDS 的扩展配置功能。 教程级别&#xff1a;高级 时间&#xff1a;20 分钟 目录 背景 先决条件在同一个节点中混合同步和异步发布 创建具有发布者的节点创建包含配置文件的 XML 文件执行发布者节点创建一个包含订阅者的节…...

VirtualBox虚拟机与主机互传文件的方法

建立共享文件夹 1.点击设置&#xff0c;点击共享文件夹&#xff0c;添加共享文件夹路径&#xff0c;保存 2.启动虚拟机&#xff0c;点击设备&#xff0c;点击安装增强功能&#xff0c;界面会出现一个光碟图标&#xff0c;点击光碟图标 3.打开光碟图标&#xff0c;出现一个目…...

访问控制系列

目录 一、基本概念 1.客体与主体 2.引用监控器与引用验证机制 3.安全策略与安全模型 4.安全内核 5.可信计算基 二、访问矩阵 三、访问控制策略 1.主体属性 2.客体属性 3.授权者组成 4.访问控制粒度 5.主体、客体状态 6.历史记录和上下文环境 7.数据内容 8.决策…...

【BUG】已解决:ModuleNotFoundError: No module named ‘cv2’

已解决&#xff1a;ModuleNotFoundError: No module named ‘cv2’ 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开…...

成都亚恒丰创教育科技有限公司 【插画猴子:笔尖下的灵动世界】

在浩瀚的艺术海洋中&#xff0c;每一种创作形式都是人类情感与想象力的独特表达。而插画&#xff0c;作为这一广阔领域中的璀璨明珠&#xff0c;以其独特的视觉语言和丰富的叙事能力&#xff0c;构建了一个又一个令人遐想连篇的梦幻空间。成都亚恒丰创教育科技有限公司 在众多插…...

gite+picgo+typora打造个人免费笔记软件

文章目录 1️⃣个人笔记软件2️⃣ 配置教程2.1 使用软件2.2 node 环境配置2.3 软件安装2.4 gite仓库设置2.5 配置picgo2.6 测试检验2.7 github教程 &#x1f3a1; 完结撒花 1️⃣个人笔记软件 最近换了环境&#xff0c;没有之前的生产环境舒适&#xff0c;写笔记也没有劲头&…...

只用 CSS 能玩出什么花样?

在前端开发领域&#xff0c;CSS 不仅仅是一种样式语言&#xff0c;它更像是一位多才多艺的艺术家&#xff0c;能够创造出令人惊叹的视觉效果。本文将带你探索 CSS 的无限可能&#xff0c;从基本形状到动态动画&#xff0c;从几何艺术到仿生设计&#xff0c;只用 CSS 就能玩出令…...

Linux C++ 056-设计模式之迭代器模式

Linux C 056-设计模式之迭代器模式 本节关键字&#xff1a;Linux、C、设计模式、迭代器模式 相关库函数&#xff1a; 概念 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种常用的设计模式。迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而…...

【Elasticsearch7.11】reindex问题

参考博文链接 问题&#xff1a;reindex 时出现如下问题 原因&#xff1a;数据量大&#xff0c;kibana的问题 解决方法&#xff1a; 将DSL命令转化成CURL命令在服务上执行 CURL命令 自动转化 curl -XPOST "http://IP:PORT/_reindex" -H Content-Type: application…...

nginx代理缓存

在服务器架构中&#xff0c;反向代理服务器除了能够起到反向代理的作用之外&#xff0c;还可以缓存一些资源&#xff0c;加速客户端访问&#xff0c;nginx的ngx_http_proxy_module模块不仅包含了反向代理的功能还包含了缓存功能。 1、定义代理缓存规则 参数详解&#xff1a; p…...

[React 进阶系列] useSyncExternalStore hook

[React 进阶系列] useSyncExternalStore hook 前情提要&#xff0c;包括 yup 的实现在这里&#xff1a;yup 基础使用以及 jest 测试 简单的提一下&#xff0c;需要实现的功能是&#xff1a; yup schema 需要访问外部的 storage外部的 storage 是可变的React 内部也需要访问同…...

Linux C++ 055-设计模式之状态模式

Linux C 055-设计模式之状态模式 本节关键字&#xff1a;Linux、C、设计模式、状态模式 相关库函数&#xff1a; 概念 状态模式&#xff08;State Pattern&#xff09;是设计模式的一种&#xff0c;属于行为模式。允许一个对象在其内部状态改变时改变它的行为。对象看起来似…...

景联文科技构建高质量心理学系知识图谱,助力大模型成为心理学科专家

心理大模型正处于快速发展阶段&#xff0c;在临床应用、教育、研究等多个领域展现出巨大潜力。 心理学系知识图谱能够丰富心理大模型的认知能力&#xff0c;使其在处理心理学相关问题时更加精确、可靠和有洞察力。这对于提高心理健康服务的质量和效率、促进科学研究以及优化教育…...

【数学建模】——数学规划模型

目录 一、线性规划&#xff08;Linear Programming&#xff09; 1.1 线性规划的基本概念 1.2 线性规划的图解法 模型建立&#xff1a; 二、整数规划&#xff08;Integer Programming&#xff09; 2.1 整数规划的基本概念 2.2 整数规划的求解方法 三、非线性规划&#x…...

卸载linux 磁盘的内容,磁盘占满

Linux清理磁盘 https://www.cnblogs.com/siyunianhua/p/17981758 当前文件夹下&#xff0c;数量 ls -l | grep "^-" | wc -l ls -lR | grep "^-" | wc -l 找超过100M的大文件 find / -type f -size 100M -exec ls -lh {} \; df -Th /var/lib/docker 查找…...

LeetCode-随机链表的复制

. - 力扣&#xff08;LeetCode&#xff09; 本题思路&#xff1a; 首先注意到随机链表含有random的指针&#xff0c;这个random指针指向是随机的&#xff1b;先一个一个节点的拷贝&#xff0c;并且把拷贝的节点放在拷贝对象的后面&#xff0c;再让拷贝节点的next指向原链表拷贝…...

axios 下载大文件时,展示下载进度的组件封装——js技能提升

之前面试的时候&#xff0c;有遇到一个问题&#xff1a;就是下载大文件的时候&#xff0c;如何得知下载进度&#xff0c;当时的回复是没有处理过。。。 现在想到了。axios中本身就有一个下载进度的方法&#xff0c;可以直接拿来使用。 下面记录一下处理步骤&#xff1a; 参考…...

Linux: network: device事件注册机制 chatGPT; notify

ChatGPT 在 Linux 内核中,有关网络设备(net-device)的事件注册机制,允许用户在网络设备的状态发生变化(例如设备被删除、添加或修改)时接收通知。这主要通过 netdev 事件通知机制实现。具体来说,内核提供了一组用于注册和处理网络设备事件的 API。 以下是一些关键组件…...

【ROS2】测试

为什么要进行自动化测试&#xff1f; 以下是我们应该进行自动化测试的许多重要原因之一&#xff1a; 您可以更快地对代码进行增量更新。ROS 有数百个包&#xff0c;具有许多相互依赖关系&#xff0c;因此很难预见一个小变化可能引起的问题。如果您的更改通过了单元测试&#xf…...

STM32智能甲鱼养殖系统设计与实现

基于STM32的智能甲鱼养殖系统设计与实现1. 项目概述1.1 系统背景现代水产养殖行业正面临从传统人工管理向智能化转型的关键时期。甲鱼作为对水质环境敏感的特种水产品&#xff0c;其养殖过程中需要持续监测多项水质参数并保持稳定环境。传统人工监测方式存在响应滞后、精度不足…...

百考通:AI赋能设计都高效落地

在数字化时代&#xff0c;市场调研、产品设计、学术研究等场景中&#xff0c;问卷设计作为核心环节&#xff0c;直接影响着数据收集的质量与工作推进的效率。传统问卷设计往往面临流程繁琐、耗时耗力、问题设计不精准等痛点&#xff0c;而百考通&#xff08;https://www.baikao…...

ssm+java2026年毕设私教预约系统【源码+论文】

本系统&#xff08;程序源码&#xff09;带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、选题背景关于会议管理问题的研究&#xff0c;现有研究主要以传统纸质登记和简单的OA系统为主&#xff0c;专门针对智能化、全流程会议预…...

Android开机向导定制实战:从源码分析到禁用状态栏的隐藏技巧

Android开机向导深度定制&#xff1a;从源码解析到状态栏控制实战 第一次接触Android开机向导定制时&#xff0c;我被这个看似简单却隐藏复杂逻辑的系统组件深深吸引。作为设备初始化的第一道门户&#xff0c;开机向导不仅承载着用户体验的第一印象&#xff0c;更是厂商品牌展示…...

PyQt新手必看:Fluent Widgets vs PyQtGraph,哪个更适合你的GUI项目?

PyQt新手指南&#xff1a;Fluent Widgets与PyQtGraph的深度对比与选型策略 当你第一次踏入PyQt GUI开发的世界&#xff0c;面对琳琅满目的框架选择&#xff0c;是否感到迷茫&#xff1f;Fluent Widgets和PyQtGraph这两个名字可能已经出现在你的搜索列表中&#xff0c;但它们究竟…...

终极指南:如何用Save Image as Type一键转换网页图片格式

终极指南&#xff1a;如何用Save Image as Type一键转换网页图片格式 【免费下载链接】Save-Image-as-Type Save Image as Type is an chrome extension which add Save as PNG / JPG / WebP to the context menu of image. 项目地址: https://gitcode.com/gh_mirrors/sa/Sav…...

你还在给每个图片父元素加类名?CSS :has() 让选择器“逆天改命”

你还在给每个图片父元素加类名&#xff1f;CSS :has() 让选择器“逆天改命” 引言 “组长&#xff0c;这个需求我写不了。” “什么需求&#xff1f;” “产品经理说&#xff0c;所有包含图片的卡片&#xff0c;要在卡片上加一个‘带图标识’的边框。但是这些卡片是动态渲染的&…...

GPT-5.4 Pro接入Java!百万上下文+电脑操控,Spring AI集成教程

文章目录前言一、先搞清楚你在驯服什么野兽二、Spring AI Alibaba是什么鬼&#xff1f;核心优势三、环境准备&#xff1a;别在JDK版本上栽跟头四、基础对话&#xff1a;先让AI开口说话五、百万上下文的正确打开方式六、Computer Use&#xff1a;让AI真的动起来实际应用场景七、…...

【手把手】FFmpeg音视频开发从入门到实战:一文吃透音视频同步原理与代码实现(附完整源码)

文章目录第一章 基础必懂&#xff1a;音视频开发的核心概念与FFmpeg框架1.1 别再被封装格式忽悠&#xff1a;MP4、MKV、AVI到底差在哪&#xff1f;1.2 搞懂解码流程&#xff1a;FFmpeg处理音视频的4个核心结构体第二章 深入原理&#xff1a;音视频同步的核心机制2.1 播放器卡顿…...

SWF逆向工程行业报告:JPEXS Free Flash Decompiler市场份额2025深度分析

SWF逆向工程行业报告&#xff1a;JPEXS Free Flash Decompiler市场份额2025深度分析 【免费下载链接】jpexs-decompiler JPEXS Free Flash Decompiler 项目地址: https://gitcode.com/gh_mirrors/jp/jpexs-decompiler 在Flash技术逐渐退出主流但仍有大量历史资产需要维护…...