`
Tyler_Long
  • 浏览: 7086 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

SQLAlchemy 数据建模过程的改进

阅读更多

SQLAlchemy是python里面最好的orm框架(注意, 没有"之一"两个字), 不过它定义orm的过程比较繁琐, 要分别定义table和model, 然后在两者之间弄个mapper. 纯手工的过程就是这样的, 一步步来, 有点体力活的感觉. 其实我没有实际写过这种代码, 因为我不喜欢干体力活.

 

#纯手工建模的代码我也没写过, 这里空缺
 

也许正是因为这个问题,很多人更喜欢django的orm, 尽管后者远远不如SQLAlchemy强大. 题外话: django的orm跟django的问题是一样的: 做些常见的东西还行, 稍微有点复杂或者稍微有点特殊的情况, 会让你很头疼. 这东西是报社开发的, 比较适合报社使用, 其它情况慎用. 不过django admin是比较牛的, 快速开发一个数据录入系统非常合适. django admin是我离开的django后唯一怀念的东西.

 

后来SQLAlchemy为了让纯手工的过程简化, 推出了一个Declarative扩展. 之所以是个扩展, 是因为它并没有增加实质的功能, 提供的价值就是帮你把建模的过程简化,实际的东西还是通过那套model, table, mapper的机制来完成的. 它可以让你只定义model, 然后table和mapper自动帮你搞定. 听起来还不错, 工作量至少减少了一半以上. 一个例子:

 

class Manufacturer(Base):
    __tablename__ = 'manufacturer'
    id = Column(Integer, primary_key = True)
    name = Column(String(30))
 

 

按道理能搞到这个程度, 比django的orm也复杂不到哪里去了, 我还是觉得有个地方不是很爽, 就是定义一对多关系的时候, 需要先定义一个外键, 然后再定义一个relationship. 总感觉有点重复: 既然我都定义了外键了, 那么当然我期望有一个relationship啊. SQLAlchemy这么设计我能理解, 它是为了更大的灵活性, 毕竟很多东西不应该一下子写死.

 

class Car(models.Model):
    __tablename__ = 'car'
    id = Column(Integer, primary_key = True)
    name = Column(String(30))

    manufacturer_id = Column(Integer, ForeignKey('manufacturer.id'))
    manufacturer = relationship('Manufacturer', backref = backref('cars', lazy = 'dynamic'))
 

 

其实这还不算太糟糕啦. 关键是, 如果Menufacturer生产了不只Car一种产品呢, 比如韩国三星, 好多产品都做, 什么电冰箱,洗衣机,显示器,内存条,光驱...etc. 在你为这些所有的东西建模的时候, 你都要为它们加上外键, 并且再加上一个relationship, 就跟上面的类似的两行代码. 这给人的感觉是明显违背了DRY(Don't Repat Yourself)原则. 像我这种软件设计的小鸟都看出问题来了, 不用说SQLAlchemy开发者也早就看出问题了, 于是他们推出了Mixin的方式来减少重复. 

 

再来段题外话. Mixin似乎是Ruby的专利(当然我没有去考究过是不是Ruby第一个有这概念的). Ruby没有多继承, 却有Mixin. Mixin能够实现多继承的大部分功能, 但是却更加简单. Ruby的作者Matz为此颇为得意. 而Python中是有多继承的, 但是却没有Ruby中Mixin的机制. 于是可以用多继承的方式来实现类似Mixin的效果, 来简化SQLAlchemy中创建一对多关系的过程.

 

class BaseMixin:
     @declared_attr
     def __tablename__(cls):
          return cls.__name__.lower()

     id =  Column(Integer, primary_key = True)

class ManufacturerMixin:
     @declared_attr
     def manufacturer_id(self):
          return Column(Integer, ForeignKey('manufacturer.id'))

     @declared_attr
     def manufacturer(cls):
          return relationship('Manufacturer', backref = backref(cls.__name__.lower() + 's', lazy = 'dynamic'))
 

 

可以看到, 只要继承了BaseMixin, 就自动会声明tablename为小写的类名, 并且自动添加了一个Integer类型的主键. 只要继承Manufacturer, 就会动拥有到menufacturer表的外键及相应的关系. 通过这种继承Mixin的方式, 极大提供了代码的可重用性, 减少了代码量:

 

class Car(Base, BaseMixin, ManufacturerMixin)
     name = Column(String(30))
 

 

非常棒! 现在Car这个类里面只剩下了一行代码了! 上述代码中一个地方不是很容易理解, 就是@declared_attr. 这是个function decorator, 看过它的源码, 实际上是个类(decorator本身可以是个类, 事实上只要是callable就可以了). 根据文档: Mark a class-level method as representing the definition of a mapped property or special declarative member name. @declared_attr is more often than not applicable to mixins, to define relationships that are to be applied to different implementors of the class. 我的理解大致就是说, 把相关属性附加到目标类上面. 毕竟Mixin是为了让别的类重用, 改变别的类才是它最终的目的. 这里就不去深究了, 再没有深入了解SQLAlchemy其它的部分的基础知识的前提下, 这个问题恐怕很难彻底搞清楚.

 

不过大家有没有一种感觉, 就是通过继承来实现Mixin的方式是可以进一步改善的? 比如上面的那个ManufactureerMixin类, 里面的代码虽然不多, 但写得很拖沓, 明明是两个字段而已, 却写成了两个方法, 并且这两个方法都加上了@declared_attr这个样的一个decorator.(并且这个decorator的真实含义不太容易弄明白, 就算你看了文档, 也只能说大致了解, 知其然不知其所以然). 于是我开始思考能不能在Python里面用另外一种方式来实现Mixin? 我想到了Python2.6中新出现的class decorator.(有人可能会问python2.5以及之前的版本怎么版呢? 我只能说凉拌, 你还用上面的通过继承来Mixin的方式就行了. Python2.6在今天已经是很古老的版本了, python2.7和python3.2稳定版都出来很久了, 谁还有心思去兼顾python2.5啊). 先上代码吧:

 

def manufacturer_mixin(cls):
          cls.manufacturer_id = Column(Integer, ForeignKey('manufacturer.id'))
          cls.manufacturer = relationship('Manufacturer', backref = backref(cls.__name__.lower() + 's', lazy = 'dynamic'))

@manufacturer_mixin
class Car(Base, BaseMixin)
          name = Column(String(30))
 

 

上面的代码是不是更加好了呢? 首先代码行短了很多, 其次添加decorator的方式似乎也比多继承要优雅些. 最让人高兴的是, 那个难以理解的"@declared_attr"不见了. 剩下的都是可以理解的代码.

 

不过现在还是不够, 因为每定义一个外键关系就得定一个相应的class decorator. 每个class decorator之中的代码都是类似的: 一个外键和一个relationship. 这显然还是不符合DRY原则的. 可不可以只定义一个class decorator就搞定所有的外键? 如果可以的话, 这个decorator可以放到通用库中, 供以后所有的需要定义数据库外键的项目中使用, 极大地提高代码的复用程度.

 

def foreign_key(table):
        """Class decorator, add a foreign key to a SQLAlchemy model.
        Parameter table is the destination table, in a one-to-many relationship, table is the "one" side.
        """
        def ref_table(cls):
            setattr(cls, '{0}_id'.format(table), Column(Integer, ForeignKey('{0}.id'.format(table))))
            setattr(cls, table, relationship(table.capitalize(), backref = backref(cls.__name__.lower() + 's', lazy = 'dynamic')))
            return cls
        return ref_table
 

 

上面的代码没有任何跟具体模型相关的代码, 所以它的通用性是非常强的. 理论上讲在所有的需要定义sqlalchemy模型的地方都可以使用, 不用一遍遍地重复代码了. 只要定义像上面的一个foreign_key class decorator, 就可以一行代码搞定一个外键关系, 比如:

 

@foreign_key('another_foreign_key')
@foreign_key('manufacturer')
class Car(Base, BaseMixin)
    name = Column(String(30))
 

 

到此为止吧. 编程有个原则, 叫做"不要过度优化"(Avoid Premature Optimization). 这个原则很多时候特指性能方面的优化. 在这里也勉强适用: 优化代码也得适可而止, 太过分的优化就是浪费时间.

 

 

 

1
2
分享到:
评论

相关推荐

    Python库 | covid_19_terms-1.0.4.tar.gz

    这个库可能是为了帮助研究人员、数据科学家以及开发者快速获取和处理与新冠病毒相关的数据,从而进行疫情分析、建模或者可视化。让我们深入探讨一下这个库可能包含的内容和相关知识点。 首先,我们要了解的是Python...

    SA

    标题中的"SA"通常在IT领域中代表"系统分析(System Analysis)",这是一门关于理解和改进组织信息系统的技术。系统分析是软件开发过程中的一个关键阶段,主要任务是对现有的业务流程进行深入研究,识别问题,提出解决...

    EHR_analysis821

    这个项目可能还会涉及到其他Python库,比如Pandas的`groupby`和`merge`功能用于数据聚合和合并,Scikit-learn的特征选择和预处理工具,以及可能的数据库操作(如SQLite或SQLAlchemy)来存储和检索大规模EHR数据。...

    sassy-bot

    此外,它可能还集成了机器学习算法,以便随着时间的推移改进其对话能力。 1. **Python基础知识**:Python是sassy-bot的基础,是一门易学且功能强大的编程语言,广泛应用于Web开发、数据分析、AI等多个领域。其简洁...

    实训商业源码-单个商品销售系统源码-毕业设计.zip

    实训商业源码-单个商品销售系统源码-毕业设计.zip

    基于KEALC编程的变压器智能温度监控系统设计与实现

    内容概要:本文详细介绍了基于KEALC编程的变压器温度实时检测与报警系统的开发过程。系统采用51单片机为核心控制器,利用DS18B20传感器测量变压器顶层油温和绕组温度,并通过LCD显示屏实时显示温度值。当温度超过设定阈值时,系统将触发报警机制,包括点亮LED灯和启动蜂鸣器。若温度达到危险水平,则自动切断继电器进行保护。文中还讨论了Proteus仿真的具体步骤、Altium Designer (AD)绘制电路图的注意事项以及实际部署中的优化措施。此外,文章强调了温度阈值的可配置性和防误报的设计思路。 适合人群:具有一定电子电路和嵌入式系统基础知识的技术人员,特别是从事电力设备维护和自动化控制系统开发的专业人士。 使用场景及目标:适用于需要对变压器温度进行精确监控的应用场合,确保变压器安全稳定运行,预防因温度过高引发的安全事故。目标是提供一种高效可靠的温度监控解决方案,保障电力设备的长期可靠运行。 其他说明:文中提供的代码片段展示了关键的报警逻辑和温度采集算法,有助于读者理解和实现类似项目。同时,文中提到的实际部署经验和优化建议对于提高系统的稳定性和可靠性具有重要指导意义。

    nacos-server-2.2.2.tar包及使用

    环境准备:Nacos 依赖 Java 环境运行,需确保安装了 64 位 JDK 1.8+。 解压安装包:在 Linux 系统中,使用命令 tar -zxvf nacos-server-2.2.2.tar.gz 解压到指定目录,如 /usr/local/nacos

    Advanced RF Board Skills in ADS.pdf

    Advanced RF Board Skills in ADS

    你好你好是多少多少多少多少多少

    你好你好是多少多少多少多少多少

    地球物理勘探基于MATLAB的射线追踪算法实现:地质结构正向建模与数据保存系统设计

    内容概要:本文档主要描述了一个基于MATLAB的射线追踪实验流程。首先定义了射线追踪结果的输出文件名为'synthetic_rays.mat',并加载了必要的观测点几何数据和正向模型数据。接着设置了射线追踪的相关参数,包括标志位、步长以及最大追踪步数等。然后通过调用mFAST_raytracing函数执行射线追踪操作,获取计算时间、成功索引和射线轨迹等结果。最后将射线追踪结果保存为.mat文件,并将射线传播时间分别保存为.mat文件和文本文件。; 适合人群:具备一定MATLAB基础,从事地质勘探、地球物理等相关领域的科研人员或学生。; 使用场景及目标:①进行地下结构成像研究;②模拟地震波传播路径;③验证正向模型准确性。; 阅读建议:此文档详细记录了射线追踪实验的具体步骤,在阅读时应重点关注参数设置部分,并结合实际应用场景调整相关参数值,同时注意保存结果的不同格式以满足不同需求。

    实训商业源码-城市生活服务分类手机页面模板-毕业设计.zip

    实训商业源码-城市生活服务分类手机页面模板-毕业设计.zip

    三菱FX5U PLC四轴定位控制系统设计与实现:涵盖参数设定、触摸屏集成及多模式控制

    内容概要:本文详细介绍了三菱FX5U PLC在四轴定位控制项目中的应用,涵盖了从参数设定到触摸屏程序整合的完整流程。首先,项目概述了使用三菱FX5U PLC作为核心控制器,负责四个轴的定位控制,并通过威纶通触摸屏实现人机交互。接下来,文章逐步讲解了公共参数设定、回原点功能、JOG手动控制、绝对和相对定位控制、多种控制模式(手动、自动、暂停)、IO表与电气选型清单、威纶通触摸屏程序开发以及电路图(EPLAN)绘制。每个环节都体现了PLC在工业自动化中的关键作用。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要深入了解PLC编程和四轴定位控制的人群。 使用场景及目标:适用于需要设计和实现四轴定位控制系统的工程项目,帮助工程师掌握PLC编程技巧,提高系统稳定性和精度。同时,也为实际项目提供了一个完整的参考模板。 其他说明:本文不仅提供了理论指导,还包括具体的实现细节和实例,有助于读者更好地理解和应用相关技术。

    蓝牙技术蓝牙4.2无线连接技术升级:速度、距离、功耗与安全性能提升及应用场景扩展综述

    内容概要:本文详细介绍了蓝牙4.2技术的发展背景、主要性能提升及其广泛应用场景。蓝牙技术自1994年由爱立信公司启动探索,经历了多个版本的迭代升级。蓝牙4.2在2014年底发布,在数据传输速度、传输距离、功耗和安全性能四个方面实现了显著提升,最大传输速度达到2.5Mbps,传输距离扩展到50米,功耗进一步降低,并引入了AES-CCM算法和数字签名等安全功能。这些改进使得蓝牙4.2在智能穿戴设备、智能家居、物联网等多个领域得到广泛应用。此外,文章还对比了蓝牙4.2与蓝牙4.0、蓝牙5.0及更高版本的差异,指出了如何挑选合适的蓝牙4.2设备,并展望了其未来在智能家居、工业物联网、虚拟现实等领域的应用前景。; 适合人群:对无线通信技术感兴趣的科技爱好者、智能家居和物联网从业者、以及希望了解蓝牙技术发展历程和最新进展的普通用户。; 使用场景及目标:①了解蓝牙技术的历史和发展趋势;②掌握蓝牙4.2相较于其他版本的具体改进;③学习如何根据需求选择合适的蓝牙4.2设备;④探索蓝牙4.2在不同领域的应用潜力。; 其他说明:蓝牙4.2虽然在某些性能上不如后续版本,但在许多对性能要求适中的场景中仍具有重要地位。未来,蓝牙4.2将继续在智能家居、工业物联网等领域发挥重要作用,并随着技术进步不断拓展新的应用场景。

    .1- 2025-5-09

    .1- 2025-5-09

    【嵌入式系统】STM32F407ZET6 GPIO接口详解:输入输出模式与应用实例介绍文档的主要内容

    内容概要:本文档详细介绍了STM32F407ZET6的GPIO(通用输入输出接口)。GPIO在输出模式下能控制端口输出高低电平,适用于驱动LED、控制蜂鸣器等;在输入模式下可读取端口的高低电平或电压,如读取按键输入、ADC电压采集等。每个通用I/O端口包含多个32位配置寄存器、数据寄存器等。文档列举了GPIO的八个功能模式,重点解析了输出状态中的推挽输出和开漏输出,以及输入状态中的下拉电阻和上拉电阻的概念与工作原理。推挽输出可输出高低电平,由两个互补的晶体管提供较大电流驱动;开漏输出通常只能输出低电平,适合电平转换。输入状态方面,下拉电阻将信号初始化为低电平,上拉电阻则初始化为高电平,其本质分别是输出和注入电流。; 适合人群:嵌入式系统开发人员、电子工程师、对STM32微控制器有兴趣的学习者。; 使用场景及目标:①帮助开发者理解STM32F407ZET6的GPIO工作机制;②为实际项目中GPIO的应用提供理论指导,如控制外部设备、读取传感器数据等。; 其他说明:文档提供了详尽的寄存器配置信息和功能模式介绍,有助于深入理解和灵活运用GPIO接口。建议读者结合实际硬件操作进行学习,以加深理解。

    PHP编程PHP语言教程与项目实战:从基础语法到进阶开发全流程指南

    内容概要:本文全面介绍了PHP语言,涵盖从基础知识到项目实战的多个方面。首先概述了PHP的特点及其在Web开发领域的应用,接着详细讲解了环境搭建的方法,包括Web服务器、PHP安装和数据库配置。文档深入浅出地阐述了PHP的基础语法,如变量、数据类型、控制结构、函数和数组操作。通过用户登录系统和数据展示页面两个实战案例,展示了PHP在实际项目中的应用,涉及数据库交互、会话管理和页面渲染。进一步探讨了进阶项目的开发流程,强调了需求分析、技术选型、MVC模式的重要性,并介绍了部署与优化的技巧。最后,推荐了主流框架(如Laravel、Symfony)、开发工具以及学习资源,帮助读者提升PHP开发技能。 适合人群:初学者以及有一定编程经验、希望深入学习PHP的开发者。 使用场景及目标:①快速搭建PHP开发环境,掌握PHP基础语法;②通过实战案例理解PHP在Web开发中的具体应用;③学习项目开发流程,提高代码质量和开发效率;④了解PHP框架和工具,拓宽技术视野。 阅读建议:阅读时应结合实际操作练习,按照文档步骤搭建环境、编写代码,同时参考提供的学习资源,逐步深入理解PHP的各项特性。

    智能车竞赛学习资料:机器人操作系统ROS原理与应用.pdf

    智能车竞赛学习资料:机器人操作系统ROS原理与应用.pdf

    实训商业源码-WordPress主题-图片摄影作品展示自适应主题-毕业设计.zip

    实训商业源码-WordPress主题-图片摄影作品展示自适应主题-毕业设计.zip

    小程序 叮咚活动报名高级版V5.2.8+前端.zip

    叮咚活动报名高级版小程序V5.2.8 前端 版本号:5.2.8 – 普通版 备注:需要重新上传小程序 1、修复票券上地址显示错误问题 2、修复其他小的BUG

    威纶通MT6103IP触摸屏与台达VFD-M变频器Modbus RTU通讯实现及操作指南

    内容概要:本文详细介绍了威纶通MT6103IP触摸屏与两台台达VFD-M变频器通过Modbus RTU进行通讯的具体实现方法和操作步骤。首先讲解了硬件连接方式,强调了正确的接线对于确保通讯正常的重要性。接着阐述了通讯参数的设置,包括波特率、数据位、停止位以及校验方式的选择,并指出这些参数在所有设备间的一致性至关重要。随后提供了具体的编程实例,如读取运行频率和发送启动命令的关键代码片段,同时解释了Modbus地址到实际寄存器地址之间的转换规则。此外还讨论了一些常见的调试技巧,例如解决RS485信号衰减的方法,在触摸屏界面上添加通讯状态监测等功能,以及针对台达变频器特有的参数写入机制进行了说明。 适用人群:工业自动化领域的工程师和技术人员,特别是那些负责PLC编程、人机界面(HMI)开发以及现场设备维护工作的专业人士。 使用场景及目标:适用于希望深入了解并掌握如何将威纶通触摸屏与台达变频器集成在一起的应用场合。主要目的是帮助用户顺利完成两者间的通信配置,确保能够可靠地监控和控制变频器的工作状态。 其他说明:文中不仅包含了理论性的指导,还有大量实用的操作提示和经验分享,有助于提高工作效率,减少调试过程中可能出现的问题。

Global site tag (gtag.js) - Google Analytics