Flask开发系列之数据库操作

Flask开发系列之数据库操作

 

Python数据库框架

我们可以在Flask中使用MySQL、Postgres、SQLite、Redis、MongoDB 或者 CouchDB。

还有一些数据库抽象层代码包供选择,例如 SQLAlchemy 和MongoEngine。你可以使用这些抽象包直接处理高等级的 Python 对象,而不用处理如表、文档或查询语言此类的数据库实体。

 

 

使用Flask-SQLAlchemy管理数据库

我们选择使用的数据库框架是 Flask-SQLAlchemy(http://pythonhosted.org/Flask-SQLAlchemy/),这个 Flask 扩展包装了 SQLAlchemy(http://www.sqlalchemy.org/)框架。
Flask-SQLAlchemy 是一个 Flask 扩展,简化了在 Flask 程序中使用 SQLAlchemy 的操作。
SQLAlchemy 是一个很强大的关系型数据库框架,支持多种数据库后台。SQLAlchemy 提供了高层 ORM,也提供了使用数据库原生 SQL 的低层功能。

安装

(venv) $ pip install flask-sqlalchemy

  

在 Flask-SQLAlchemy怎么使用数据库

在 Flask-SQLAlchemy 中,数据库使用 URL 指定。最流行的数据库引擎采用的数据库 URL。

FLask-SQLAlchemy数据库URL如下所示:

数据库引擎 URL
MySQL mysql://username:password@hostname/database
Postgres postgresql://username:password@hostname/database
SQLite(Unix) sqlite:////absolute/path/to/database
SQLite(Windows) sqlite:///c:/absolute/path/to/database

在这些 URL 中,hostname 表示 MySQL 服务所在的主机,可以是本地主机(localhost),
也可以是远程服务器。数据库服务器上可以托管多个数据库,因此 database 表示要使用的
数据库名。如果数据库需要进行认证,username 和 password 表示数据库用户密令。

注意:SQLite 数据库不需要使用服务器,因此不用指定 hostname、username 和password。URL 中的 database 是硬盘上文件的文件名。

 

配置数据库

程序使用的数据库 URL 必须保存到 Flask 配置对象的 SQLALCHEMY_DATABASE_URI 键中。配置对象中还有一个很有用的选项,即 SQLALCHEMY_COMMIT_ON_TEARDOWN 键,将其设为 True时,每次请求结束后都会自动提交数据库中的变动。其他配置选项的作用请参阅 Flask-SQLAlchemy 的文档。

test.py

from flask_sqlalchemy import SQLAlchemy
from flask import Flask
import os

basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)

 

db 对象是 SQLAlchemy 类的实例,表示程序使用的数据库,同时还获得了 Flask-SQLAlchemy提供的所有功能。

 

定义模型

模型这个术语表示程序使用的持久化实体。在 ORM 中,模型一般是一个 Python 类,类中的属性对应数据库表中的列。

test.py:定义 Role 和 User 模型

class Role(db.Model):
  __tablename__ = 'roles'
  id = db.Column(db.Integer, primary_key=True)
  name = db.Column(db.String(64), unique=True)
  def __repr__(self):
    return '<Role %r>' % self.name
class User(db.Model):
  __tablename__ = 'users'
  id = db.Column(db.Integer, primary_key=True)
  username = db.Column(db.String(64), unique=True, index=True)
  def __repr__(self):
    return '<User %r>' % self.username

类变量 __tablename__ 定义在数据库中使用的表名。如果没有定义 __tablename__ ,Flask-SQLAlchemy 会使用一个默认名字,但默认的表名没有遵守使用复数形式进行命名的约定,所以最好由我们自己来指定表名。

其余的类变量是该模型的属性,被定义为 db.Column类的实例。

注意:Flask-SQLAlchemy 要求每个模型都要定义 主键 ,这一列经常命名为 id 。

 

db.Column 类构造函数的第一个参数是数据库列和模型属性的类型。
最常用的SQLAlchemy列类型
类型名         Python类型     说  明
Integer        int         普通整数,一般是 32 位
SmallInteger    int         取值范围小的整数,一般是 16 位
BigInteger    int 或 long     不限制精度的整数
Float        float         浮点数
Numeric        decimal.Decimal     定点数
String        str         变长字符串
Text        str         变长字符串,对较长或不限长度的字符串做了优化
Unicode        unicode         变长 Unicode 字符串
UnicodeText    unicode         变长 Unicode 字符串,对较长或不限长度的字符串做了优化
Boolean        bool         布尔值
Date        datetime.date     日期
Time        datetime.time     时间
DateTime        datetime.datetime     日期和时间
Interval        datetime.timedelta     时间间隔
Enum        str         一组字符串
PickleType     任何 Python 对象     自动使用 Pickle 序列化
LargeBinary    str         二进制文件



db.Column 中其余的参数指定属性的配置选项。列出了一些可用选项。
最常使用的SQLAlchemy列选项
选项名         说  明
primary_key     如果设为 True ,这列就是表的主键
unique         如果设为 True ,这列不允许出现重复的值
index         如果设为 True ,为这列创建索引,提升查询效率
nullable         如果设为 True ,这列允许使用空值;如果设为 False ,这列不允许使用空值
default         为这列定义默认值

 

数据库操作

创建表

(venv) $ python test.py shell
>>> from hello import db
>>> db.create_all()

如果修改模型后要把改动应用到现有的数据库中,这一特性会带来不便,它把数据库中原有的数据都销毁了。更新现有数据库表的粗暴方式是先删除旧表再重新创建:
>>> db.drop_all()
>>> db.create_all()

 

插入行

下面这段代码创建了一些角色和用户:
>>> from hello import Role, User
>>> admin_role = Role(name='Admin')
>>> mod_role = Role(name='Moderator')
>>> user_role = Role(name='User')
>>> user_john = User(username='john', role=admin_role)
>>> user_susan = User(username='susan', role=user_role)
>>> user_david = User(username='david', role=user_role)

通过数据库会话管理对数据库所做的改动,在 Flask-SQLAlchemy 中,会话由 db.session表示。准备把对象写入数据库之前,先要将其添加到会话中:
>>> db.session.add(admin_role)
>>> db.session.add(mod_role)
>>> db.session.add(user_role)
>>> db.session.add(user_john)
>>> db.session.add(user_susan)
>>> db.session.add(user_david)
或者简写成:
>>> db.session.add_all([admin_role, mod_role, user_role,
... user_john, user_susan, user_david])
为了把对象写入数据库,我们要调用 commit() 方法提交会话:
>>> db.session.commit()

注意:数据库会话能保证数据库的一致性。提交操作使用原子方式把会话中的对象全部写入数据库。如果在写入会话的过程中发生了错误,整个会话都会失效。如果你始终把相关改动放在会话中提交,就能避免因部分更新导致的数据库不一致性。
数据库会话也可 回滚 。调用 db.session.rollback() 后,添加到数据库会话中的所有对象都会还原到它们在数据库时的状态。

 

修改行

在数据库会话上调用 add() 方法也能更新模型。我们继续在之前的 shell 会话中进行操作,
下面这个例子把 "Admin" 角色重命名为 "Administrator" :
>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()

 

删除行

数据库会话还有个 delete() 方法。下面这个例子把 "Moderator" 角色从数据库中删除:
>>> db.session.delete(mod_role)
>>> db.session.commit()
注意,删除与插入和更新一样,提交数据库会话后才会执行。

 

查询行

1.Flask-SQLAlchemy 为每个模型类都提供了 query 对象。最基本的模型查询是取回对应表中的所有记录:

>>> Role.query.all()

[<Role u'Administrator'>, <Role u'User'>]

>>> User.query.all()
[<User u'john'>, <User u'susan'>, <User u'david'>]

 

2.使用过滤器可以配置 query 对象进行更精确的数据库查询。下面这个例子查找角色为"User" 的所有用户:

>>> User.query.filter_by(role=user_role).all()
[<User u'susan'>, <User u'david'>]

 

3.若要查看 SQLAlchemy 为查询生成的原生 SQL 查询语句,只需把 query 对象转换成字符串:

>>> str(User.query.filter_by(role=user_role))

'SELECT users.id AS users_id, users.username AS users_username,

users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'

 

4.如果你退出了 shell 会话,前面这些例子中创建的对象就不会以 Python 对象的形式存在,而

是作为各自数据库表中的行。如果你打开了一个新的 shell 会话,就要从数据库中读取行,
再重新创建 Python 对象。下面这个例子发起了一个查询,加载名为 "User" 的用户角色:
>>> user_role = Role.query.filter_by(name='User').first()

 

5.filter_by() 等过滤器在 query 对象上调用,返回一个更精确的 query 对象。多个过滤器可以一起调用,直到获得所需结果。下表列出了可在 query 对象上调用的常用过滤器。完整的列表参见 SQLAlchemy 文档

(http://docs.sqlalchemy.org)。

常用的SQLAlchemy查询过滤器

过滤器 说  明
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit() 使用指定的值限制原查询返回的结果数量,返回一个新查询
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询

 

6.在查询上应用指定的过滤器后,通过调用 all() 执行查询,以列表的形式返回结果。除了all() 之外,还有其他方法能触发查询执行。下表 列出了执行查询的其他方法。

最常使用的SQLAlchemy查询执行函数

方 法 说  明
all() 以列表形式返回查询的所有结果
first() 返回查询的第一个结果,如果没有结果,则返回 None
first_or_404() 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应
get() 返回指定主键对应的行,如果没有对应的行,则返回 None
get_or_404() 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错误响应
count() 返回查询结果的数量
paginate() 返回一个 Paginate 对象,它包含指定范围内的结果

 

 

 

 

(1) SQLAlchemy操作sql原生

安装操作数据库的模块

pip3 install pymysql

安装 flask-sqlalchemy

pip3 install flask-sqlalchemy

配置路径

DB_URI = ‘mysql+pymysql://root:password@host:port/database’

下面先看下sqlalchemy操作的写法:

from sqlalchemy import create_engine

HOST = '127.0.0.1'
USERNAME = 'root'
PASSWORD = '123456'
DATABASE = 'demo'  #数据库名
PORT = 3306
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOST,PORT,DATABASE)
#创建引擎
engine = create_engine(DB_URI)

with engine.connect() as db:
    data = db.execute('select * from user') #从user表中获取全部数据
    db.execute('delete from user where id=1')  #删除id=1的数据

(2) 设计数据表

1 字段类型

类型名python中的类型说明
Integer int 存储整形 32位
SmallInteger int 小整形 16为
BigInteger int 大整形
Float float 浮点数
String str 字符串 varchar
Text str 长文本
Boolean bool bool值
Date datetimedate 日期
Time datetime.time 时间
datetime datetime.datetime 时间日期

2 可选条件

选项说明
primary_key 主键, 如果设为True,表示主键
unique 唯一索引 ,如果设为True,这列唯一
index 常规索引, 如果设为True,创建索引,提升查询效率
nullable 是否可以为null 默认True
default 默认值

(3)在flask中使用ORM模型

下面我们使用ORM模型

from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:20111673@127.0.0.1:3306/demo'
db = SQLAlchemy(app)  #

manager = Manager(app)

#创建User用户,表名为user
class User(db.Model):
    __table__name = 'user'
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20),index=True)
    sex = db.Column(db.Boolean,default=True)
    info = db.Column(db.String(50))

# 定义一个视图函数
@app.route('/create')
def create():
    # db.drop_all()  #删除仅为模型表
    db.create_all()  #创建模型表
    return '创建成功'

if __name__ == '__main__':
    manager.run()

(4)增加数据

添加数据方式1

#方式1
# sqlalchemy默认开启了事务处理
@app.route('/insert/')
def insert():
    try:
        u = User(username='WANGWU',info='personal WANGWU message')
        db.session.add(u)  #添加数据对象
        db.session.commit()  #事务提交
    except:
        db.session.rollback()#事务回滚
    return '添加单条数据!'

@app.route('/insertMany/')
def insertMany():
    u1 = User(username='name1',info='personal name1 message')
    u2 = User(username='name2',info='personal name2 message')
    db.session.add_all([u1,u2]) #以add_all(数据对象列表)
    db.session.commit() #
    return '添加多条数据!'

添加数据方式2

#方式2
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True  #在app设置里开启自动提交
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  #关闭数据追踪,避免内存资源浪费

@app.route('/insertMany/')
def insertMany():
    u1 = User(username='name1',info='personal name1 message')
    u2 = User(username='name2',info='personal name2 message')
    db.session.add_all([u1,u2])
    return '提交多条数据'

(5)更新与删除

# 类名.query  返回对应的查询集
# 类名.query.get(查询条件)  返回对应的查询对象
@app.route('/update/')
def update():
    u = User.query.get(1)
    u.username = 'update name'  #更新内容
    db.session.add(u)   #进行添加
    return 'update'

# 删除数据
@app.route('/delete/')
def delete():
    u = User.query.get(2)  #找到对应的查询集对象
    db.session.delete(u)  # 删除对应的u对象
    return 'delete id=2'

(6) 拆分MVT

目录结构

project/
    manage.py  #启动项存放
    ext.py  #作为当前sqlalchemy扩展
    settings.py  #配置存放
    app/
        __init__.py
        models.py  #应用models.py
        views.py   #应用视图views.py
    templates/     #模板目录
    static/  #静态文件目录

ext.py SQLAlchemy扩展

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()   #实例化db对象

蓝本view view.py视图函数

from flask import Blueprint
from .models import User
from ext import db
#创建蓝本view
view = Blueprint('view',__name__)
#定义视图函数
@view.route('/')
def index():
    return 'index'

@view.route('/insert/')
def insert():
    u = User(username='张三',info='个人信息')
    db.session.add(u)
    return 'insert success'

蓝本view models.py模型类

from ext import db  #导入db
#构建User模型类
class User(db.Model,Base):
    __table__name = 'user'
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20),index=True)
    sex = db.Column(db.Boolean,default=True)
    info = db.Column(db.String(50))

manage.py启动项

from flask import Flask
from flask_script import Manager
from ext import db
import settings
from app.view import view

app = Flask(__name__)
#将系统配置项Config类加载到app
app.config.from_object(settings.Config)
#通过db对象将app初始化
db.init_app(app)
#将蓝图view注册进app
app.register_blueprint(view)
manager = Manager(app)

if __name__ == '__main__':
    manager.run()

setting.py配置文件

class Config:
    #设置mysql+pymysql的连接
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:20111673@127.0.0.1:3306/demo'
    #加密设置
    SECRETE_KEY = 'secret_key'
    #关闭数据追踪
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    #开启提交
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True

前面我们采用系统的每次自动提交session 即SQLALCHEMY_COMMIT_ON_TEARDOWN

但是如果想自己定义提交方式,同时不想传入关键字参数,那么该怎样入手呢?这里提供一种思路

(7) 自定义增删改类

我们对模型类进行了修改,models.py 内容如下:

from ext import db
#定义了base基类
class Base:
    def save(self):
        try:
            db.session.add(self)  #self实例化对象代表就是u对象
            db.session.commit()  
        except:
            db.session.rollback()
    #定义静态类方法接收List参数        
    @staticmethod
    def save_all(List):
        try:
            db.session.add_all(List)
            db.session.commit()
        except:
            db.session.rollback()
    #定义删除方法
    def delete(self):
        try:
            db.session.delete(self)  
            db.session.commit()  
        except:
            db.session.rollback()
#定义模型user类
class User(db.Model,Base):
    __table__name = 'user'
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(20),index=True)
    sex = db.Column(db.Boolean,default=True)
    info = db.Column(db.String(50))
    #
    def __init__(self,username='',info='',sex=True):
        self.username = username
        self.info = info
        self.sex = sex
#注意:
#原实例化:  u = User(username='张三',info='个人信息')
#现实例化: u = User('李四','李四个人信息')

在views.py中使用

from flask import Blueprint
from .models import User
from ext import db

view = Blueprint('view',__name__)

@view.route('/')
def index():
    return 'index'
#插入单条数据
@view.route('/insert/')
def insert():
    # u = User(username='test',info='default')
    u = User('xiaomeng','default')
    u.save()
    db.session.add(u)
    return 'insert success'
#保存多条数据
@view.route('/saveMany/')
def saveMany():
    u1 = User('zhan123','default123')
    u2 = User('li123','default message')
    User.save_all([u1,u2])
    return 'add many'
#删除数据
@view.route('/delete/')
def delete():
    u = User.query.get(1)  #获取查询集
    u.delete()
    return 'delete message'

其他都不做改变,基本思路是封装到类,通过多继承来实现方法的调用。

 

转载:https://yq.aliyun.com/articles/610817

posted @ 2019-06-21 20:57  -零  阅读(730)  评论(0编辑  收藏  举报