sql(structured query language),用来查询和操作 数据库。常见的sql很难和python集成在一起,安全性能也不好,用起来不方便。

sqlalchemy是一个pyhton库,使用户可以直接用python语句进行查询等操作,可连接各种关系型数据库,如Postgres, MySQL, SQlite, Oracle等。

sqlalchemy 对各种数据库的sql进行抽象,组成了一套更高级的 通用语法。无论使用哪种数据库,都能正确编译(通用语法是我们用的,数据库执行的是 编译后的sql)。这也使数据在 不同数据库之间 的转移变得简单。

sqlalchemy包含两套语句:Core(sql表达式语言,是ORM的基础) 和 ORM(抽象程度比Core更高,贴近python的类)。可以混合使用;同时也允许用最基础的sql语言

注意区分,sql表达式语言(core,以sql语言为基础) 和 sql语言(底层)

即,底层sql 抽象 --> core , core 抽象--> orm


新建一个文件夹,创建虚拟环境。在虚拟环境里安装sqlalchemy

pip install sqlalchemy

安装数据库。sqlalchemy直接支持sqlite3,不需额外安装(pip install pysqlite3)。连接其他数据库则需要安装对应的数据库驱动。

pip install psycopg2    #PostgreSQL
pip install pymysql    #MySQL

创建sqlalchemy引擎。manage.py

#本地SQlite数据库 引擎(数据库不存在时,会自动创建)
from sqlalchemy import create_engine    #create_engine只是返回一个引擎实例,在进行需要连接的操作之前不会打开连接

engine = create_engine('sqlite:///cookies.db')    #相对路径
#engine = create_engine('sqlite:///:memory:')    #内存数据库,每次执行都新建,执行完清除
#engine = create_engine('sqlite:home/cookiemonster/cookies.db')    #Unix下的绝对路径
#engine = create_engine('sqlite:///c:\\Users\\cookiemonster\\cookies.db')    #windows下的绝对路径
'''
#建一个本地PostgreSQL数据库 引擎
from sqlalchemy import create_engine
engine = create_engine('postgresql+psycopg2://username:password@localhost:5432/mydb')
#建一个远程MySQL数据库 引擎
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://cookiemonster:chocolatechip@mysql01.monster.internal/cookies',pool_recycle=3600)    #pool_recycle默认值-1,表示不超时
'''

 引擎 包装了 数据库连接池和方言,使我们的python代码不必考虑数据库之间的差异。 

有了引擎,才能 连接数据库connect()。manage.py

from sqlalchemy import create_engine

engine = create_engine('sqlite:///cookies.db')
connection = engine.connect()

 连接数据库以后,才能使用Core和ORM


sqlalchemy为了访问底层数据库,需要有某种东西来代表数据库中的 表,这样才能在ide里编辑。可以使用以下三种来代表:

1 Table。 这个是Core用的

2 类。ORM用的

3 从数据库里反射。


表数据类型。sqlalchemy支持4种数据类型:

1 通用类型。从各数据库支持的 实际sql类型中 抽象出来综合到一起(这一点使得通用类型很强大,编译的时候用户 不需考虑太多细节,sqlalchemy自己会处理不同数据库的区别)。

2 SQL标准类型。一些通用类型里 没有的类型。全部采用大写字母。

3 厂商自定义类型。只适用于 特定的后端数据库。全部采用大写字母。

4 用户定义类型。


元数据对象。一个 记录 Table对象的 目录(记录目前有哪些Table,以便sqlalchemy能快速访问。用MetaData.tables访问),其中还包含与 引擎和连接有关的信息

不用太深究,建表的时候把metadata作为参数带上就行了。

初始化元数据,用于保存上述的数据库结构(表+引擎+连接)。manage.py

from sqlalchemy import MetaData

metadata = MetaData()

建表。manage.py

from sqlalchemy import Table, Column, Integer, Numeric, String, ForeignKey, Boolean
from datetime import datetime
from sqlalchemy import DateTime

cookies = Table('cookies', metadata,        #带上元数据
    Column('cookie_id', Integer(), primary_key=True),    #设为主键
    Column('cookie_name', String(50), index=True),    #为这一列创建 索引,加快该列的查询速度? 索引在迁移里被看作和表 并列的一种变更,详见[sqlalchemy12 创建迁移]
    Column('cookie_recipe_url', String(225)),    #字符长度最大255
    Column('cookie_sku', String(55)),
    Column('quantity', Integer()),
    Column('unit_cost', Numeric(12,2))  #长度12、精度2位小数
)

users = Table('users', metadata,
    Column('user_id', Integer(), primary_key=True),
    Column('customer_number', Integer(), autoincrement=True),
    Column('username', String(15), nullable=False, unique=True),    #非空、唯一
    Column('email_address', String(255), nullable=False),
    Column('phone', String(20), nullable=False),
    Column('password', String(25), nullable=False),
    Column('created_on', DateTime(), default=datetime.now),
    Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now),
    )

以上部分一直用 Colume里的 列关键字参数(String,nullable等)来定义 表的 结构约束。也可以在Colume外部Table内部)进行声明:比如已经有一个数据库了,现在要额外添加 模式结构约束,则要在外部添加。例,常见的3种约束

from sqlalchemy import PrimaryKeyConstraint, UniqueConstraint, CheckConstraint
PrimaryKeyConstraint('user_id', name='user_pk')    #主键可以在 表构造好之后 再外部定义
#可以添加 多个由都逗号分隔的列来 创建 复合主键
#name参数为可选,此约束的数据库内名称
UniqueConstraint('username', name='uix_username') #唯一性约束。确保不存在重复的两个值。
CheckConstraint('unit_cost >= 0.00', name='unit_cost_positive')    #检查约束。确保 数据 与 用户标准 相匹配

索引。用来加快 对应列 的查找速度。比如cookie_name加上了索引,是因为我们经常用cookie的名字去搜索,加上索引会使这个搜索的速度加快。

除了在Column里加index=True,还可以在Table内部显式创建索引

from sqlalchemy import Index
Index('ix_cookies_cookie_name', 'cookie_name')

关系外键约束

关联表:用于支持两个其他表之间的 多对多关系。如果一个表上 存在多个外键关系,则很可能是 关联表。manage.py

orders = Table('orders', metadata,
    Column('order_id', Integer(), primary_key=True),
    Column('user_id', ForeignKey('users.user_id')),    #用字符串 而不用实际列users.c.user_id,就可以不必担心表的加载顺序问题
    Column('shipped', Boolean(), default=False),
    )

#关联表
line_items = Table('line_items', metadata,
    Column('line_items_id', Integer(), primary_key=True),
    Column('order_id', ForeignKey('orders.order_id')),
    Column('cookie_id', ForeignKey('cookies.cookie_id')),
    Column('quantity', Integer()),
    Column('extended_cost', Numeric(12,2)),
    )

字符串 可以允许我们跨多个模块分离表定义,不必担心表的加载顺序。如果使用硬引用如cookies.c.cookie_id,则会在模块初始化期间 执行解析,并可能失败,这取决于表的加载顺序。

还可以在Column外部(对应的Table的内部)添加 外键约束。

from sqlalchemy import ForeignKeyConstraint
line_items = Table('line_items', metadata,
    Column('line_items_id',....)
    #没有Column('order_id',....)
    #...
    Column('extended_cost', Numeric(12,2))
    ForeignKeyConstraint(['order_id'],['orders.order_id'])
)

将 表持久化 到数据库中。create_all()不会尝试 重新创建 数据库中已有的表,可以安全地运行多次。这些在flask已经学到了,当表的属性有变化时,可以使用 数据库迁移工具(12章)。manage.py

metadata.create_all(engine)

完整代码。manage.py

from sqlalchemy import create_engine
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer, Numeric, String, ForeignKey, Boolean
from datetime import datetime
from sqlalchemy import DateTime

engine = create_engine('sqlite:///cookies.db')
connection = engine.connect()

metadata = MetaData()

cookies = Table('cookies', metadata,
    Column('cookie_id', Integer(), primary_key=True),
    Column('cookie_name', String(50), index=True),
    Column('cookie_recipe_url', String(225)),
    Column('cookie_sku', String(55)),
    Column('quantity', Integer()),
    Column('unit_cost', Numeric(12,2))  #长度12、精度2位小数
)

users = Table('users', metadata,
    Column('user_id', Integer(), primary_key=True),
    Column('customer_number', Integer(), autoincrement=True),
    Column('username', String(15), nullable=False, unique=True),
    Column('email_address', String(255), nullable=False),
    Column('phone', String(20), nullable=False),
    Column('password', String(25), nullable=False),
    Column('created_on', DateTime(), default=datetime.now),
    Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now),
    )

orders = Table('orders', metadata,
    Column('order_id', Integer(), primary_key=True),
    Column('user_id', ForeignKey('users.user_id')),
    Column('shipped', Boolean(), default=False),
    )

line_items = Table('line_items', metadata,
    Column('line_items_id', Integer(), primary_key=True),
    Column('order_id', ForeignKey('orders.order_id')),
    Column('cookie_id', ForeignKey('cookies.cookie_id')),
    Column('quantity', Integer()),
    Column('extended_cost', Numeric(12,2)),
    ForeignKeyConstraint(['order_id'],['orders.order_id'])
    )

metadata.create_all(engine)