对象关系映射

sqlalchemy是一种常用的python orm框架,orm即对象关系映射(Object Relational Mapping,简称ORM)。在应用程序处理数据库相关的写、改、删、查等工作流程时,一般可以使用字符串拼接的形式来构造sql语句来执行相关任务。但这样也有一些缺点,首先拼接字符串可能会遇到sql注入的问题,会对系统的安全性带来负面影响。其次一些表的名称可能会以硬编码的形式留在代码里,不利于数据库与程序的维护。除此之外,还可以利用orm框架来操纵数据库,既可以减小安全方面的漏洞,又可以利用高级语言面向对象的特性,还可以不用重复造轮子。

最近在使用sqlite的时候采用了sqlalchemy作为orm框架,用起来感觉还比较好用,至少比手动构造sql语句方便多了。

安装

sqlalchemy安装很简单,就是标准的python包安装方式。

pip install SQLAlchemy

简单使用方法

定义模型

ORM(sqlalchemy)一个重要特点就是将数据库的数据结构与Python类联系起来,将一行数据与对象联系起来。数据在SQL数据库中是以表、行、列的形式组织起来的,使用orm的关键就是数据模型的定义。数据库结构模型既可以从头开始定义,又可以从已有的数据库中提取抽象出来。现在只考虑从零开始的数据库。

在定义模型之前,首先明确使用的是哪一种数据库,不同的SQL数据库的功能会留有差别,不过ORM从设计目的上来看应该是尽可能为不同的数据库后端提供一套差不多的API。

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine("sqlite+pysqlite:///wxdog.db", echo=True)
db_session = scoped_session(sessionmaker(autocommit=False,autoflush=False,bind=engine))
Base = declarative_base()

这是定义数据库后端的代码这里的sqlite+pysqlite:///wxdog.db是数据库字符串URL,表明采用的是sqlite数据库,数据库文件是wxdog.db。这段代码中导出了两个重要的东西db_sessionBase,一个是数据库的连接对象,另一个是与数据库结构有关的对象,把这端定义数据库后端的代码放到一个文件里,在其他的py文件中只要import这两个东西就可以用了,非常方便。

定义好后端之后就可以的定义数据库结构了。数据库模型定义一般放在一个单独的文件(models.py)里

from sqlalchemy import Column, Integer, String, Text ,Boolean
from sqlalchemy.sql.sqltypes import DateTime, Float
from .db import Base
import string

class Tdog(Base):
    __tablename__ = 'texts'
    id = Column(String(16), primary_key=True)
    url = Column(String(512), unique=False)
    pubtime= Column(String(16), unique=False)
    stime= Column(DateTime, unique=False)
    uid= Column(String(16), unique=False)
    ak= Column(String(256), unique=False)
    title = Column(String(512), unique=False)
    ctext = Column(Text, unique=False)
    ptext = Column(Text, unique=False)
    author = Column(String(256), unique=False)
    desp = Column(Text, unique=False)

从这个例子中可以看到,类与数据库中的表对应,类的实例对象与数据库中的行对应,Column与数据库中是列对应。

应当注意到,定义的类继承的是Base这个类,这里把它和数据库后端的定义放一起了。在定义完成数据库的基本结构定义之后之后,还可以定义一些类方法进行格式化输出或者对相关变量进行更改。

例如

class Dog(Base):
    __tablename__ = 'dogs'
    id = Column(Integer, primary_key=True)
    username = Column(String(length=30), unique=True)
    password = Column(String(length=128), unique=False)
    outlink = Column(String(length=20), unique=False)
    token = Column(String(length=20), unique=False)
    aurl = Column(String(512), unique=False)
    text = Column(Text, unique=False)
    isadmin = Column(Boolean,default=False)

    def new_token(self,ntoken=None):
        if ntoken is None:
            self.token=''.join(random.sample(string.ascii_letters + string.digits, 10))
        else:
            self.token=ntoken
        return self.token

    def ddog(self):
        return {"id":self.id,"text":str(self.text),'aurl':self.aurl,'username':self.username,'outlink':self.outlink}

初始化数据库

完成定义之后,数据库文件并没有建立起来,需要手动对数据库进行初始化

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine("sqlite+pysqlite:///wxdog.db", echo=True)
db_session = scoped_session(sessionmaker(autocommit=False,autoflush=False,bind=engine))
Base = declarative_base()
from . import models
Base.metadata.create_all(bind=engine)

对数据库的数据库还可以通过alembic这个数据库结构版本控制工具来完成,这个后面会讲。

基本操作

数据库基本操作,无非就是写、改、删、查。

from models import Tdog
from db import db_session
new_dog=Tdog(id=1,stime=datetime.now(),uid=1,url="https://www.dogcraft.top/")
db_session.add(new_dog)
db_session.commit()

这里用到了Tdogdb_session这两个分别来自在数据库模型定义与数据库后端定义。

sdl=Tdog.query.order_by(Tdog.pubtime.desc()).filter(Tdog.url!=None).limit(10).offset(page*10)
nnu=Tdog.query.count()
udog=Dog.query.filter(Dog.token==i).first()

filter来代替sql中的where语句,如果符合条件的结果有多个,则会返回一个list,如果最后有frist()就会返回第一个对象。其中filter可以多个串列起来进行链式过滤,也可以使用and_ or_等逻辑运算符来进行连接。

rdog=Tdog.query.filter(Tdog.id==dogid).first()
rdog.url="https://www.dogcraft.cn"
rdog.title="title"
rdog.ctext="dog dog dog"
rdog.pubtime=datetime.now()
rdog.stime=datetime.now()
rdog.ak="22235151451sdkkl"
db_session.commit()

先查询,把相关行对象化,然后像修改python对象一样修改,最后提交到数据库里面去。

asp=Tdog.query.filter(Tdog.id==c).first()
db_session.delete(asp)
db_session.commit()

先查后删

版本控制

数据库的结构并不是一成不变的,而是会随着后续的功能增加而动态变化,在改变数据库结构时并不能把原有的数据弃之不顾,需要在原有数据库的基础上进行改动,一般可以采用手写SQL语句来完成,但也可以利用一些工具自动化完成,只需要改动模型定义部分即可,alembic就是这样一个与sqlalchemy配套使用的数据库版本控制工具。

安装很简单,直接用pip就可以了。安装完成之后进行初始化

alembic init alembic

这个时候当前目录下会出现alembic.ini配置文件与同名目录,在alembic.ini里面进行数据库路径配置

sqlalchemy.url = sqlite+pysqlite:///wxdog.db

一般只需要改动这里即可,内容应该与sqlalchemy后端写法一致。

到这里基本配置就结束了,但如果要想自动化的生成数据库结构变更操作,还需要进行更高级的配置。要编辑alembic目录下的evn.py文件,import数据库结构定义模块,并得到metadata即可。

from myapp import mymodel
target_metadata = mymodel.Base.metadata

完成配置之后就可对数据库结构模型进行改动了,改完models.py之后,运行

alembic revision --autogenerate -m "message"

即可生成相应的更新文件。

然后运行

alembic upgrade head

将改动实际贯彻落实到数据库中。

如果应用版本控制工具来部署相关应用,alembic目录及配置文件应当随着代码一同发布,部署端在更新时只需alembic upgrade head即可。即使在使用sqlite时没有数据库文件,alembic也会自动新建一个。