翼度科技»论坛 编程开发 python 查看内容

Python工具箱系列(三十四)

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
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上创建表格,并且可以查询表格的元数据,以及插入数据后的查询。
  1. from sqlalchemy import (Column, Integer, MetaData, String, Table,
  2.                         create_engine, text, Float, DateTime, ForeignKey)
  3. from sqlalchemy_utils.functions import create_database, database_exists
  4. configure_pg = {"user": "postgres",
  5.                 'password': '88488848',
  6.                 'dns': 'dbserver.home',
  7.                 "port": 5432,
  8.                 'prefix': 'postgresql+psycopg2',
  9.                 'postfix': ''
  10.                 }
  11. configure_mssql = {"user": "sa",
  12.                    'password': '88488848',
  13.                    'dns': 'dbserver.home',
  14.                    "port": 1433,
  15.                    'prefix': 'mssql+pymssql',
  16.                    'postfix': '?charset=utf8'
  17.                    }
  18. configure_mysql = {"user": "root",
  19.                    'password': '88488848',
  20.                    'dns': 'dbserver.home',
  21.                    "port": 3306,
  22.                    'prefix': 'mysql+mysqlconnector',
  23.                    'postfix': ''
  24.                    }
  25. config = {'mssql': configure_mssql,
  26.           'mysql': configure_mysql, 'postgresql': configure_pg}
  27. database_name = 'testdb'
  28. table_sensor_location = "sensor_location"
  29. table_sensor_data = "sensor_data"
  30. def linkdb(targetstr):
  31.     """
  32.     连接不同的数据库
  33.     Args:
  34.         targetstr (string): 数据库名称
  35.     Returns:
  36.         engine: 用于后续的数据库连接
  37.     """
  38.     if targetstr in config.keys():
  39.         item = config[targetstr]
  40.         connectstring = f"{item['prefix']}://{item['user']}:{item['password']}@{item['dns']}:{item['port']}/{database_name}{item['postfix']}"
  41.         engine = create_engine(connectstring, echo=True, future=True)
  42.     # 如果数据库不存在,则创建之
  43.     if not database_exists(engine.url):
  44.         create_database(engine.url)
  45.     # 做一个测试,不针对任何表
  46.     with engine.connect() as conn:
  47.         result = conn.execute(text("select 'hello world'"))
  48.         print(result.all())
  49.     return engine
  50. def createtbs(connector):
  51.     """"
  52.     创建数据库中的2张表。用于保存传感器数据与传感器本身的信息
  53.     """
  54.     metadata_obj = MetaData()
  55.     # 描述传感器的表
  56.     sensor_location_tb = Table(
  57.         table_sensor_location,
  58.         metadata_obj,
  59.         Column('id', Integer, primary_key=True, autoincrement=False),
  60.         Column('location', String(30), nullable=False)
  61.     )
  62.     # 保存传感器数据的表
  63.     sensor_data_tb = Table(
  64.         table_sensor_data,
  65.         metadata_obj,
  66.         Column('id', Integer, primary_key=True, autoincrement=False),
  67.         Column('sensor_id', ForeignKey(
  68.             f'{table_sensor_location}.id'), nullable=False),
  69.         Column('area', String(30)),
  70.         Column('pm25', Float),
  71.         Column('timestamp', DateTime)
  72.     )
  73.     print(sensor_data_tb.compile())
  74.     # 创建并返回表
  75.     metadata_obj.create_all(connector)
  76.     return sensor_data_tb, sensor_location_tb
  77. def tableinfo(connector, tablename):
  78.     """
  79.     获得指定表名的相关元数据信息
  80.     Args:
  81.         connector (engine): 数据库连接器
  82.         tablename (string): 要查询的表名
  83.     """
  84.     metadata_obj = MetaData()
  85.     some_table = Table(tablename, metadata_obj, autoload_with=connector)
  86.     print([c.name for c in some_table.columns])
  87. def gensonsorinfo(connector):
  88.     with connector.connect() as conn:
  89.         conn.execute(text(f"INSERT INTO {table_sensor_location} (id, location) VALUES (:x, :y)"),
  90.                      [{"x": 1, "y": '1号楼'}, {"x": 2, "y": '2号楼'}])
  91.         conn.commit()
  92.         result = conn.execute(
  93.             text(f"SELECT id, location FROM {table_sensor_location}"))
  94.         for x, y in result:
  95.             print(f"id: {x}  location: {y}")
  96. # 依次连接多个数据库。从而验证代码的一致性
  97. for dbname in config.keys():
  98.     con = linkdb(dbname)
  99.     createtbs(con)
  100.     tableinfo(con, table_sensor_data)
  101.     tableinfo(con, table_sensor_location)
  102.     gensonsorinfo(con)
复制代码
从代码可以看出,可以用统一的访问方式来操作mssql/mysql/postgresql三种数据库。而且,以上方式与前文中的基于游标的写法类似。
 

来源:https://www.cnblogs.com/shanxihualu/p/17440033.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具