最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

Python工具箱系列(三十四)

来源:博客园

SQLAlchemy是著名的ORM(Object Relational Mapping-对象关系映射)框架。其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。对许多语言(例如JAVA/PYTHON)来说就是定义一个对象,并且这个对象对应着一张数据库的表。而这个对象的实例,就对应着表中的一条记录。

其整体思路如下图所示:


(资料图片仅供参考)

其中类、对象与属性与数据库相关内容的对应关系如下图所示:

ORM的优点:

  • 数据模型与代码统一定义,更新与维护简单,代码高度重用一致。

  • ORM有现成的工具,很多功能都可以自动完成,比如表格增删、预处理、事务等。

  • 基于ORM的业务代码比较简单,代码量少,语义性好,容易理解。

  • 你不必编写性能不佳的SQL。

ORM的缺点:

  • ORM库多层封装,实现巧妙,需要花很多精力学习和设置。

  • 对于复杂的查询,ORM要么是无法表达,要么是性能不如原生的SQL。

  • ORM抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的SQL。

从整体上看,ORM节省了开发时间,减少了代码错误的可能性,同时能够方便地在多个数据库间灵活迁移,还是非常值得使用。而在python语言中,SQLAlchemy是著名的ORM框架之一,它的整体架构如下图所示:

从图中可以看出,SQLAIchemy是分层架构,由Core以及ORM两部分组成。其中,Core完成了与数据库操作的各类封闭,是相对低层的。而ORM层则利用Core层的能力进行更宏观的操作。因此,在一段python代码中,使用Core与ORM层同时来操作数据库也是可行的,并不矛盾与冲突。

下面先从最基本的表格创建做起。非ORM编程中,表格的创建无非两个途径:

●基于DBMS本身提供的CLI/GUI界面,发出DDL语句进行数据库/表格本身的增删改查。

●使用语言连接数据库后,发出命令来对数据库/表格进行增删改查。

而由于每种数据库都有自己的方言,所以命令语句各有差异,需要不断地调整。而使用SQLAlchemy则实现了代码统一。例如以下代码在mssql以及mysql上创建表格,并且可以查询表格的元数据,以及插入数据后的查询。

from sqlalchemy import (Column, Integer, MetaData, String, Table,                        create_engine, text, Float, DateTime, ForeignKey)from sqlalchemy_utils.functions import create_database, database_existsconfigure_pg = {"user": "postgres",                "password": "88488848",                "dns": "dbserver.home",                "port": 5432,                "prefix": "postgresql+psycopg2",                "postfix": ""                }configure_mssql = {"user": "sa",                   "password": "88488848",                   "dns": "dbserver.home",                   "port": 1433,                   "prefix": "mssql+pymssql",                   "postfix": "?charset=utf8"                   }configure_mysql = {"user": "root",                   "password": "88488848",                   "dns": "dbserver.home",                   "port": 3306,                   "prefix": "mysql+mysqlconnector",                   "postfix": ""                   }config = {"mssql": configure_mssql,          "mysql": configure_mysql, "postgresql": configure_pg}database_name = "testdb"table_sensor_location = "sensor_location"table_sensor_data = "sensor_data"def linkdb(targetstr):    """    连接不同的数据库    Args:        targetstr (string): 数据库名称    Returns:        engine: 用于后续的数据库连接    """    if targetstr in config.keys():        item = config[targetstr]        connectstring = f"{item["prefix"]}://{item["user"]}:{item["password"]}@{item["dns"]}:{item["port"]}/{database_name}{item["postfix"]}"        engine = create_engine(connectstring, echo=True, future=True)    # 如果数据库不存在,则创建之    if not database_exists(engine.url):        create_database(engine.url)    # 做一个测试,不针对任何表    with engine.connect() as conn:        result = conn.execute(text("select "hello world""))        print(result.all())    return enginedef createtbs(connector):    """"    创建数据库中的2张表。用于保存传感器数据与传感器本身的信息    """    metadata_obj = MetaData()    # 描述传感器的表    sensor_location_tb = Table(        table_sensor_location,        metadata_obj,        Column("id", Integer, primary_key=True, autoincrement=False),        Column("location", String(30), nullable=False)    )    # 保存传感器数据的表    sensor_data_tb = Table(        table_sensor_data,        metadata_obj,        Column("id", Integer, primary_key=True, autoincrement=False),        Column("sensor_id", ForeignKey(            f"{table_sensor_location}.id"), nullable=False),        Column("area", String(30)),        Column("pm25", Float),        Column("timestamp", DateTime)    )    print(sensor_data_tb.compile())    # 创建并返回表    metadata_obj.create_all(connector)    return sensor_data_tb, sensor_location_tbdef tableinfo(connector, tablename):    """    获得指定表名的相关元数据信息    Args:        connector (engine): 数据库连接器        tablename (string): 要查询的表名    """    metadata_obj = MetaData()    some_table = Table(tablename, metadata_obj, autoload_with=connector)    print([c.name for c in some_table.columns])def gensonsorinfo(connector):    with connector.connect() as conn:        conn.execute(text(f"INSERT INTO {table_sensor_location} (id, location) VALUES (:x, :y)"),                     [{"x": 1, "y": "1号楼"}, {"x": 2, "y": "2号楼"}])        conn.commit()        result = conn.execute(            text(f"SELECT id, location FROM {table_sensor_location}"))        for x, y in result:            print(f"id: {x}  location: {y}")# 依次连接多个数据库。从而验证代码的一致性for dbname in config.keys():    con = linkdb(dbname)    createtbs(con)    tableinfo(con, table_sensor_data)    tableinfo(con, table_sensor_location)    gensonsorinfo(con)

从代码可以看出,可以用统一的访问方式来操作mssql/mysql/postgresql三种数据库。而且,以上方式与前文中的基于游标的写法类似。

关键词: