ORM: SQLAlchemy 初识

栏目: Python · 发布时间: 5年前

内容简介:ORM: Object Relational Mapper.目前Python有很多ORM工具可以将数据库映像为Python的Objects对象。其中比较知名的有Django的ORM,SQLAlchemy, PostgreSQL等。

ORM: Object Relational Mapper.

目前 Python 有很多ORM工具可以将数据库映像为Python的Objects对象。

其中比较知名的有Django的ORM,SQLAlchemy, PostgreSQL等。

SQLAlchemy 有更多的人维护,功能也比较齐全。所以一般是我们的首选项。

对于 SQLAlchemy 的使用者来说,只要你一开始连接上数据库,不管是Sqlite,MySQL还是什么,后面的处理方式完全一样。这种便利性也是它受欢迎的原因。

抛弃了传统的自己编织 SQL 语句、制作模型、连接数据库方式, SQLAlchemy 直接把这些东西全包在黑盒里面,让我们完全不需要去管。连SQL-Injection注入这种东西也被它帮忙防范了。这样一来,可以说在连接数据库方面,帮我们节省了最少一半以上的代码。

甚至连数据查询,SQLAlchemy也代替了SQL语句,而使用了专门的类似 MongoDBObject.query.filter_by(name='Jason').all() 这种方法。

安装:

# 安装sqlalchemy
$ pip install sqlalchemy

安装Drivers:

# Sqlite
# 不需要,Python自带

# MySQL
$ pip install pymysql

# Postgresql
$ pip install psycopg2

SQLAlchemy自身不带数据库driver,需要我们自己安装,并在连接时候指定。

而这些driver,实际上就是我们曾经手动连接数据库所用的包。而SQLAlchemy只是代替我们使用这些同样的包。

连接数据库

创建一个 sqlite 的ORM引擎:

from sqlalchemy import create_engine

# 连接格式为:sqlite://<Hostname>/<path>
engine  = create_engine('sqlite:///foo.db', echo=True)

创建一个 MySQL 的ORM引擎:

from sqlalchemy import create_engine

# 连接格式为:dialect+driver://username:password@host:port/database
engine  = create_engine('mysql+pymysql://root:password123@localhost/db_test_01', echo=True)

数据库的位置(三斜杠为相对路径,四斜杠为绝对路径):

# 使用绝对路径的数据库文件(////),如/tmp/mydatabase.db
engine  = create_engine('sqlite:////tmp/mydatabase.db')

# 使用当前「执行位置」数据库文件(///或///./)
engine  = create_engine('sqlite:///mydatabase.db')

# 使用当前「执行位置」父级目录(///../)的数据库文件
engine  = create_engine('sqlite:///../mydatabase.db')

# 使用当前「脚本位置」的数据库文件
import os
cwd = os.path.split(os.path.realpath(__file__))[0]
engine  = create_engine('sqlite:///{}/mydatabase.db'.format(cwd))

Create Tables 创建表

注意:不同于SQL语句,SQLAlchemy中的表名是 完全区分大小写的

创建一个Schema表(指单纯表,不包含ORM对象):

from sqlalchemy import create_engine, MetaData
from sqlalchemy import Table, Column
from sqlalchemy import Integer, String, ForeignKey

engine  = create_engine('mysql+pymysql://root:password123@localhost/db_test_01', echo=True)
metadata = MetaData(engine)

# 创建一个表
user_table = Table( 'tb_user', metadata,
        Column('id', Integer, primary_key=True),
        Column('name', String(50)),
        Column('fullname', String(100))
)

# 让改动生效
metadata.create_all()

创建一个ORM对象(包括表):

# 导入表格创建引擎
from sqlalchemy import create_engine
# 导入列格式
from sqlalchemy import Column, Integer, String, ForeignKey
# 导入创建ORM模型相关
from sqlalchemy.ext.declarative import declarative_base

Base  = declarative_base()

class User(Base):
    __tablename__ = 'tb_Person'

    id = Column('id', Integer, primary_key=True)
    username = Column('username', String, unique=True)

engine = create_engine('sqlite:///test.sqlite', echo=True)
Base.metadata.create_all(bind=engine)

用普通表Table和ORM对象创建的表有什么不同?

他们在数据库中创建的,是完全相同的表!唯一区别是,Table创建的不包含ORM对象,也就是不提供让你直接操作Python对象的功能。

这么做的好处是,有很多只是关联作用的表,没有必要生成ORM对象。

删除数据库中的表

# engine = ...
# Base = ...

# 逐个ORM对象删除对应的表,如User类
User.__table__.drop(engine)

# 删除全部表
Base.metadata.drop_all(engine)

设计或调试过程中,我们经常要频繁改动表格,所以有必要在创建表格前把测试数据库中的表都清除掉,再创建新的定义。

Insertion 插入数据

将数据添加到数据库:

# ...
# 导入session相关(用于添加数据)
from sqlalchemy.orm import sessionmaker, relationship

user = User()
user.id = 1
user.username = 'Jason'

Session = sessionmaker(bind=engine)
session = Session()
session.add(user)
session.commit()
session.close()

注意:这里的 session 和网站上的session概念有点不一样。这里是用来commit提交数据库变动的工具。

批量添加数据(向add_all()传入列表):

session.add_all( [user1, user2, user3] )

添加每条数据的时候自动flush():

session = sessionmaker(bind=engine, autoflush=True)

autoflush 是在每次 session.add() 自动执行 session.flush() ,即在插入数据库之前就在内存中生成所有对象的动态数据(如主键ID等)。一般默认是选false,因为会影响效率。最好是需要的时候,才手动执行 session.flush()

具体缘由,看下一节“数据生效”。

Take effect 数据生效

SQLAlchemy中的 create_all()session.commit() 都是直接让python文件中定义的对象在数据库中生效的语句。在此之前,无论怎么定义,数据都是在内存中,而没有在数据库中的。

注意区分:

create_all
session.commit()

这两个的顺序,当然是先创建表格,再插入数据。

只是,如果我们知道了这个原理,在编码中才能比较运用自由。比如,连 create_engine() 创建引擎,我们都可以在后面定义,而没必要非得写在文件头,即所有的ORM定义之前。

create_engine 只要定义在所有ORM类和Schema表之后即可。

此后,我们再开始进行数据插入工作,也就利用到了session。

session过程中呢,我们也会遇到互相引用主键外键ID的情况。但是注意,这时候因为还没有使用最终的 session.commit() 真正提交数据到数据库中,这些ID是没有值的。

解决办法就是利用内置的方法 session.flush() ,将session中已添加的所有对象填充好数据,但是这时候还没有提交到数据库,只是我们内部可以正常访问各种ID了。

更新/删除数据

更新:

# Get a row of data
me = session.query(User).filter_by(username='Jason').first()

# Method 1:
me.age += 1
session.commit()

# Method 2:
session.query().filter(
    User.username == 'Jason'
).update(
    {"age": (User.age +1)}
)
session.commit()

# Method 3:
setattr(user, 'age', user.age+1)
session.commit()

Get Primary Key Value 获取主键值

#sqlalchemy can't get primary key , #sqlalchemy 如何获得主键的值

这个问题花了我很多时间探索查询,不得其解,才明白原来是很显然的事。

参考思否:SQLAlchemy中返回新插入数据的id?

虽然在没有用session或engine插入数据之前,我们可以直接浏览从ORM创建的对象中的属性值。

但是这个时候无论如何都获取不到 primar_key 主键列的值。

因为这时候主键还没有插入数据库,作为 动态的值 ,在数据库没生效之前也就为None。

为什么需要获取 value of primary_key ?考虑如下这些场景:

foreign key

那么该怎么获取主键ID呢?

如果要想在插入数据之前就获取主键等 动态列 的值,那么有这几种方法:

  • 直接利用SQLAlchemy建立类直接的内部关联,而不直接使用ID
  • 主表插入数据,另session生效后,再用query获取相应对象,来得到它的ID。
  • (*) 主表先用 session.add(..) ,再 session.flush() ,然后就可以获取ID,最后再 session.commit()
  • 不使用primary key主键,自己手动创建ID,这样来随便获取。

推荐做法如下:

ORM: SQLAlchemy 初识

即每次新创建对象后,立刻session.add(..),然后立刻session.flush(),全部都添加好的文末,再session.commit().

Query 查询

注意:query是通过session进行的,也就是必须在 session.commit() 之后才能进行查询,否则会报错。

这里将的query查询,指的都是 在插入到数据库生效之后 。理解这个很重要,因为在对象未插入到数据库之前,很多主键、外键等内容都是不存在的,也就无法查询到。

参考:pythonsheets - Object Relational basic query

查询数据:

session.commit()
# ...

users = session.query(User).all()
# 返回的是多个User类的对象:>>> [ <User 1>, <User 2>, .... ]

for u in users:
    print(u.id, u.username)

常用查询方法:

# 获取某ORM中数据 .query(ORM类名)
>>> session.query( User ).all()     # All rows of data
>>> session.query( User ).first()    # First row of data as an object

# 查询结果排序 .order_by(类名.列名)
>>> session.query(User).order_by( User.birth ).all()

# 筛选结果 .filter( True/False 表达式 )
>>> session.query(User).filter( User.name != 'Jason' ).all()
>>> session.query(User).filter( User.name.like('%ed%') ).all()    # Fuzzy search
>>> session.query(User).filter( User.id in [1, 2, 3] ).all()    # IN
>>> session.query(User).filter( ~ User.id in [4, 5, 6] ).all()   # NOT IN
>>> session.query(User).filter( User.school == 'MIT', User.age < 24 ).first()   # AND
>>> session.query(User).filter( _or(User.school == 'MIT', User.age < 24) ).first()   # OR

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

恰如其分的软件架构

恰如其分的软件架构

George Fairbanks / 张逸、倪健、高翌翔 / 华中科技大学出版社 / 2013-9-1 / 88.00

本书描述了一种恰如其分的软件架构设计方法。作者建议根据项目面临的风险来调整架构设计的成本,并从多个视角阐述了软件架构的建模过程和方法,包括用例模型、概念模型、域模型、设计模型和代码模型等。本书不仅介绍方法,而且还对方法和概念进行了归类和阐述,将软件架构设计融入开发实践中,与 敏捷开发方法有机地结合在一起,适合普通程序员阅读。 . 这是一本超值的书,案例丰富有趣,言简意赅,阅读轻松。当年......一起来看看 《恰如其分的软件架构》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具