SQLAlchemy是和很多数据库进行交互的一个库,他可以让你创建model,让你可以以一种Python中面向对象的方式进行查询。使得你的代码和数据库可以分开,也就是减轻他们之间的依赖。让你进行数据库的切换或者迁移是很方便的。

首先,你需要考虑的是使用SQLAlchemy Core还是SQLAlchemy ORM,这两种方式在解析方面稍有不同。但是最大的不同是访问是基于schema还是业务对象:

SQLAlchemy Core: 基于schema的,就有点类似于传统的SQL,在数据仓库,报表分析等方面能够对查询控制的更好是很有用的。

SQLAlchemy ORM: 但是如果在考虑领域模型的设计时,ORM封装了大量底层的schema和元数据结构,这种封装使得开发人员和数据库的交互变得更加简单
本文以sqlite数据库为例
Metadata
它经常适合数据库结构绑定在一起的,以便在SQLAlchemy能迅速的访问它。可以把它看做很多Tables 对象的集合,定义表对象之前,需要先实例化它

Tables
Table对象可以通过表名,metadata和额外Cloumn参数进行构建,Column代表着数据库中每一个字段。

举个例子:

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

metadata = MetaData()
cookies = Table('cookie',metadata,
    Column('cookie_id',Integer(),primary_key=True),
    Column('cookie_name',String(),index=True),
    Column('cookie_recipe_url',String(255)),
    Column('cookie_sku',String(55)),
    Column('quantity',Integer()),
    Column('unit_cost',Numeric(12,2))
    )
'''
primary_key: 表示这个字段或者这个列是主键
index:表示该列是索引列
'''

列定义了表中的字段,并且可以设置数据类型以及是否是主键,是否允许为空等,还具有设置默认值;不同的数据类型,可能有不同参数,比如字符串类型,可以设置长度,比如浮点类型可以设置精度等

from datetime import datetime
from sqlalchemy import DateTime
users = Table('users', metadata,
Column('user_id', Integer(), primary_key=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))
'''
nullable: 表示该字段是否允许为null
default:表示如果该字段没有设置值的时候的默认值
onupdate:每次更新时都会调用该方法或函数
这里default,onupdate属性是一个callable对象而不是直接值,比如datetime.now(),因为这样的话,就永远是这个值,而不是每个实例实例化、更新时的时间了。
'''

Keys and Constraints键和约束
键和约束是一种可以使得我们的数据满足特定的需求,键和约束既可以像上面那样通过kwargs定义在Column中,也可以在之后通过对象添加。

from sqlalchemy import PrimaryKeyConstraint,Unique
Constraint,CheckConstraint

最常用的key可能是主键primary key了,他表示该字段必须为是唯一的,你也可以定义一个复合主键通过多个列

PrimaryKeyConstraint('user_id',name='user_pk')

还有比较常用的是唯一约束和检查约束

UniqueConstraint('user_name',name="uix_username")
CheckConstraint('unit_cost',name='unit_cost_positive')
Relationships and ForeignKeyConstraints

关联关系和外键约束:

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))
)

表的持久化
所有的表和额外的schema定义都是和metadata实例相关联的,通过调用metadata.create_all(engine)方法,就可以持久化schema到数据库了

from datetime import datetime
from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String,
DateTime, ForeignKey, create_engine)
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(255)),
Column('cookie_sku', String(55)),
Column('quantity', Integer()),
Column('unit_cost', Numeric(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'))
)
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))
)
engine = create_engine('sqlite:///:memory:')
metadata.create_all(engine)

插入数据
构建一个INSERT 语句将数据插入到表中:

# 第一种
ins1 = items.insert().values(
    title = "Aldssd dsdfdf ert sunshine ",
    sell_point = "clearance!!!",
    price = "3450",
    num = "999",
    barcode = "#1234565656#",
    image = "http://image.taotao.com/jd/4ef8861cf6854de9889f3db9b24dc371.jpg",
    cid = "560",
    status = "1"
)
# 通过连接执行先前创建的插入语句
conn.execute(ins1)
# 第二种
ins2 = insert(items).values(
    title="Thinking In Java ",
    sell_point="clearance!!!",
    price="68",
    num="78",
    barcode="#1234565656#",
    image="http://image.taotao.com/jd/4ef8861cf6854de9889f3db9b24dc371.jpg",
    cid="560",
    status="2"
)
# 通过连接执行先前创建的插入语句
conn.execute(ins2)

# 第三种
ins3 = items.insert()
# 通过连接执行先前创建的插入语句
conn.execute(
    ins3, # 第一参数是一个需要执行的insert声明语句的函数的引用
    title="Scala In Action",
    sell_point="clearance!!!",
    price="168",
    num="56",
    barcode="#1234565656#",
    image="http://image.taotao.com/jd/1118861cf6854de9129f3db9b24dc371.jpg",
    cid="560",
    status="3"
)
# 第四种:同时挿入多条
ins4 = items.insert()
data_list = [
    {
        "title": "Scala In Action",
        "sell_point": "clearance!!!",
        "price": "168",
        "num": "56",
        "barcode" : "#233456124",
        "image" : "http://image.taotao.com/jd/1118861cf6854de9129f3db9b24dc371.jpg",
        "cid" : "560",
        "status" : "4"
    },
   {
        "title": "Hadoop In Action",
        "sell_point": "clearance!!!",
        "price": "868",
        "num": "77",
        "barcode" : "#233456112",
        "image" : "http://image.taotao.com/jd/1118861cf6854de9129f3db9b24dc371.jpg",
        "cid" : "561",
        "status" : "5"
    }
]
# 通过连接执行先前创建的插入语句
conn.execute(ins4,data_list)

更新数据
更新数据和插入数据其实差不多:
第一:需要使用update函数构造一个更新语句
第二:需要用where从句指定需要修改的条件
表对象.c : 表示表的列对象
表对象.c.quantity : 表示该表的哪一列

upt = update(cookies).where(cookies.c.cookie_name == 'chocolate chip')
upt = upt.values(quantity=(cookies.c.quantity + 120))
result = conn.execute(upt)
print("结果数量 => %s" %result.rowcount)

删除数据
第一: 需要使用delete函数构造一个删除语句
第二:使用where条件去过滤哪些数据需要被删粗
第三:如果没有指定where从句,那么会删除表中所有数据

# 删除数据
from sqlalchemy import delete
d1 = delete(cookies).where(cookies.c.cookie_name == 'dark chocolate chip')
d2 = delete(cookies)

result1 = conn.execute(d1)
result1 = conn.execute(d2)

查询数据
我们需要使用select函数构造一个查询语句构造一个类似于标准SELECT语句

在查询中控制列
‘’’
我们可以限制查询中返回多少字段,我们需要把这些字段传递给select方法
‘’’

s = select([items.c.id,items.c.title,items.c.price,items.c.num])
proxy = conn.execute(s)
# proxy.keys(): 可以取出我需要访问哪些列
record = proxy.first()
print(record)
# (1, 'Aldssd dsdfdf ert sunshine ', 3450, 999)

3.4.3 结果集排序

'''
对结果集排序,如果不指定排序规则,默认是升序排序
我们可以通过asc或者desc对象进行包装,然后进行圣湖或者降序排序
'''
from sqlalchemy import asc,desc

s = select([items.c.id,items.c.title,items.c.price,items.c.num])
# 根据price列进行排序
s = s.order_by(items.c.price)
proxy = conn.execute(s)
records = proxy.fetchall()
for r in records:
    print(r)

'''
或者你也可以你这么写
s = select([items.c.id,items.c.title,items.c.price,items.c.num]).order_by(items.c.price)
'''
# 根据price列进行降序排序
s = select([items.c.id,items.c.title,items.c.price,items.c.num]).order_by(desc(items.c.price))
proxy = conn.execute(s)
records = proxy.fetchall()
for r in records:
    print(r)

内置函数和别名

'''
使用sqlalchemy的内置函数,比如sum(),avg() 还可以对结算结果取别名
'''
from sqlalchemy import func
s1 = select([func.sum(items.c.price).label('total_price')])
s2 = select([func.count(items.c.title).label('count')])
proxy1 = conn.execute(s1)
proxy2 = conn.execute(s2)
record1 = proxy1.first()
record2 = proxy2.first()
print(record1.total_price,record2.count)

过滤

'''
where语句进行结果集过滤
'''
from sqlalchemy import func
s = select([items.c.title,items.c.price,items.c.sell_point]).where(items.c.price == 868)
proxy = conn.execute(s)
records = proxy.fetchall()
for record in records:
    # 返回一个元组列表,每一个元组都是列名和该列的值
    print(record.items())
# [('title', 'Hadoop In Action'), ('price', 868), ('sell_point', 'clearance!!!')]

3.4.7CluaseElements
从句元素时我们只能在从句中使用的那些实体元素,比如like()进行模糊匹配等,以下举几个例子:

'''
where语句进行结果集过滤
'''
s = select([items.c.title,items.c.price,items.c.sell_point])
# 模糊查询 like() 大小写敏感 ilike()大小写不敏感
slike = s.where(items.c.title.like('%_in action%'))
# 范围查询 between
sbetween = s.where(items.c.price.between(100,200))
# in([list])
sin = s.where(items.c.price.in_([168,868]))
# is_(None):取出为空的数据
sisnone = s.where(items.c.sell_point.is_(None))
# startswith字符串以什么开始
sstart = s.where(items.c.price.title.startswith('clearance'))
# endswith字符串以什么结束
send = s.where(items.c.price.title.endswith('clearance'))

连词

from sqlalchemy import and_,or_,not_
s = select([items.c.title,items.c.price,items.c.sell_point])
# 模糊查询 like() 大小写敏感 ilike()大小写不敏感
s_and = s.where(
    and_(
        items.c.title.ilike('%_inaction%'),
        items.c.price < 200
    )
)
s_or = s.where(
    or_(
        items.c.title.contains('Scala'),
        items.c.price > 800
    )
)
s_not = s.where(
    not_(items.c.price < 3000)
)

join 操作

conn  = engine.connect()
# 指定腰查询的列
columns = [orders.c.order_id,users.c.username,users.c.phone,
           cookies.c.cookie_name,line_items.c.quantity,line_items.c.extended_cost]
cookiemon_order = select(columns)
# 通过select_from查询数据
cookiemon_order = cookiemon_order.select_from(orders.join(users).join(line_items).join(cookies)).where(users.c.username == 'cookiemon')
results = conn(cookiemon_order).fetchall()
for row in results:
    print(row)

利用原始的查询语句进行查询和text查询

# 利用原始的查询语句进行查询
results  = conn.execute("SELECT * FROM employee").fetchall()
for row in results:
    print(row)

# 利用text查询
stmt = select([emp]).where(text("dname='DELIVERY'"))
results = conn.execute(stmt).fetchall()
for row in results:
    print(row)

事务
支持回滚等操作

def transactiontest(self,project,values):
        try:
            trans = self.connection.begin()
            for item in values:
                self.connection.execute(project.insert(),item)
                raise Exception("error")
            trans.commit()
        except:
            trans.rollback()

封装后的代码

dokcer sqlserver arm架构 sqlalchemy core orm_字段


dsserver.py

from warnings import resetwarnings
from sqlalchemy import create_engine,select
from Model.models import metadata,cookies,users,orders,line_items
from sqlalchemy.sql import func
class DBServer:
    def __init__(self):
        self.engine = create_engine("sqlite:///test.db",pool_recycle=30)
        metadata.create_all(bind=self.engine)
        self.connection = self.engine.connect()
        
    def insert_one(self,project,values):
        insert = project.insert()
        result = self.connection.execute(insert,values)
        print(result)
        
    def query(self,project):
        s = select([project])
        result = self.connection.execute(s)
        print (result.fetchall())
        # fetchall 查找所有结果
        # first 若有记录则返回第一条并关闭连接,
        # fetchone 返回一行并保持连接
        # scalar 返回第一行第一列的值
    def query_columns(self,columns):
        s = select(columns)
        s = s.limit(1)
        result = self.connection.execute(s)
        print(result.first())
        
    def func_test(self,columns):
        print(self.connection.execute(select(func.sum(columns))).scalar())
        
    def filter_test(self,project,condition):
        result =  self.connection.execute(select([project]).where(condition))
        print(result)
    
    def transactiontest(self,project,values):
        try:
            trans = self.connection.begin()
            for item in values:
                self.connection.execute(project.insert(),item)
                raise Exception("error")
            trans.commit()
        except:
            trans.rollback()

models.py

from sqlalchemy import (MetaData,Table,Column,Integer,ForeignKey,
                        String,DateTime,Numeric,Boolean)
from sqlalchemy.sql.schema import Index
from datetime import datetime
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(255)),
        Column('cookie_sku', String(55)),
        Column('quantity', Integer()),
        Column('unit_cost', Numeric(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()),
    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))
)

main.py

from Model.models import *
from DB.dsserver import DBServer


def insert():
    dal = DBServer()
    inventory_list = [
        {
            'cookie_name': 'peanut butter',
            'cookie_recipe_url': 'http://some.aweso.me/cookie/peanut.html',
            'cookie_sku': 'PB01',
            'quantity': '24',
            'unit_cost': '0.25'
        },
        {
            'cookie_name': 'oatmeal raisin',
            'cookie_recipe_url': 'http://some.okay.me/cookie/raisin.html',
            'cookie_sku': 'EWW01',
            'quantity': '100',
            'unit_cost': '1.00'
        }
    ]
    
    dal.insert_one(cookies,inventory_list)
    
    
    
    
    customer_list = [
        {
            'username': "cookiemon",
            'email_address': "mon@cookie.com",
            'phone': "111-111-1111",
            'password': "password"
        },
        {
            'username': "cakeeater",
            'email_address': "cakeeater@cake.com",
            'phone': "222-222-2222",
            'password': "password"
        },
        {
            'username': "pieguy",
            'email_address': "guy@pie.com",
            'phone': "333-333-3333",
            'password': "password"
        }
    ]

    dal.insert_one(users, customer_list)
    

    order_items = [
        {
            'order_id': 'wlk001',
            'cookie_id': 1,
            'quantity': 2,
            'extended_cost': 1.00
        },
        {
            'order_id': 'wlk001',
            'cookie_id': 3,
            'quantity': 12,
            'extended_cost': 3.00
        }
    ]
    dal.insert_one(line_items, order_items)

    dal.insert_one(orders,{"user_id":2, "order_id":'ol001'})
    
    order_items = [
        {
            'order_id': 'ol001',
            'cookie_id': 1,
            'quantity': 24,
            'extended_cost': 12.00
        },
        {
            'order_id': 'ol001',
            'cookie_id': 4,
            'quantity': 6,
            'extended_cost': 6.00
        }
    ]
    dal.insert_one(line_items, order_items)
    

    
def query():
    DBServer().query(cookies)

def query_columns():
    DBServer().query_columns([cookies.c.cookie_name,cookies.c.cookie_id])
    
def functest():
    DBServer().func_test(cookies.c.quantity)


def filters():
    DBServer().filter_test(cookies,cookies.c.cookie_name.like('%cho%'))
# insert()
# query()
# query_columns()
functest()
filters()

inventory_list = [
        {
            'cookie_name': '3',
            'cookie_recipe_url': 'http://some.aweso.me/cookie/peanut.html',
            'cookie_sku': 'PB01',
            'quantity': '24',
            'unit_cost': '0.25'
        },
        {
            'cookie_name': '4',
            'cookie_recipe_url': 'http://some.okay.me/cookie/raisin.html',
            'cookie_sku': 'EWW01',
            'quantity': '100',
            'unit_cost': '1.00'
        }
    ]

DBServer().transactiontest(cookies,inventory_list)