1. 什么是ORM
对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。
它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
2. flask-sqlalchemy
Flask-SQLAlchemy 是一个Flask 扩展,简化了在Flask 程序中使用SQLAlchemy 的操作。SQLAlchemy 是一个很强大的关系型数据库框架,支持多种数据库后台。
SQLAlchemy 提供了高层ORM,也提供了使用数据库原生SQL 的低层功能。
3. flask-sqlalchemy配置
数据库URI :SQLALCHEMY_DATABASE_URI
URI参数格式:
Postgres:postgresql://scott:tiger@localhost/mydatabase
MySQL:mysql://scott:tiger@localhost/mydatabase
Oracle: oracle://scott:tiger@127.0.0.1:1521/sidname
SQLite:sqlite:////absolute/path/to/foo.db
多个数据库支持 :
SQLALCHEMY_BINDS = {
'db1': 'mysqldb://localhost/users',
'db2': 'sqlite:////path/to/appmeta.db'
}
4. 数据库模型设计
绑定到Flask对象 :db = SQLAlchemy(app)
ORM模型创建:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
指定表的名称:__tablename__ = 'mall_product'
创建和删除表
手动创建数据库:
创建表 :db.create_all(bind=‘db1’)
删除表: db.drop_all()
数据库迁移框架:Flask 程序还可使用Flask-Migrate扩展。这个扩展对SQLAlchemy中的Alembic 做了轻量级包装,并集成到Flask-Script 中,所有操作都通过Flask-Script 命令完成。
创建迁移仓库:
配置Flask-Migrate:
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
使用init 子命令创建迁移仓库:
python xxx.py db init
创建迁移脚本:自动创建的迁移会根据模型定义和数据库当前状态之间的差异生成upgrade() 和downgrade() 函数的内容。
migrate 子命令用来自动创建迁移脚本:
python xxx.py db migrate -m "initial migration"
更新数据库:
python xxx.py db upgrade
ORM模型字段类型支持
一对多关系,外键关联
addresses = db.relationship('UserAddress', backref='person', lazy=True)
大多数情况下,db.relationship() 都能自行找到关系中的外键,但有时却无法决定把哪一列作为外键,如果无法决定外键,你就要为db.relationship() 提供额外参数,从而确定所用外键。
backref :在关系的另一个模型中添加反向引用
lazy : 指定如何加载相关记录。可选值有select(首次访问时按需加载)、immediate(源对象加载后就加载)、joined(加载记录,但使用联结)、subquery(立即加载,但使用子查询),
noload(永不加载)和dynamic(不加载记录,但提供加载记录的查询)
secondary:指定多对多关系中关系表的名字
order_by: 指定关系中记录的排序方式
多对多关联
tags = db.Table('mall_product_tags',db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')), db.Column('product_id', db.Integer,db.ForeignKey('product.id')) )
5. 使用ORM插入,修改,删除数据
新增/修改数据
构造ORM模型对象:
user = User('admin', 'admin@example.com')
添加到db.session(备注:可添加多个对象):
db.session.add(user)
提交到数据库:
db.session.commit()
物理删除数据
查询ORM模型对象
user=User.query.filter_by(username='zhangsan').first()
添加到db.session
db.session. delete(user)
提交到数据库
db.session.commit()
6. 使用ORM查询数据并展示
ORM查询
返回结果集(list)
查询所有数据
User.query.all()
按条件查询
User.query.filter_by(username='zhangsan') User.query.filter(User.nicknae.endswith('三')).all()
排序
User.query.order_by(User.username)
查询TOP10
User.query.limit(10).all()
返回单个ORM对象
根据pk查询
User.query.get(1)
获取第一条记录
User.query.first()
视图快捷函数: 有则返回,无则返回404
first() vs first_or_404()
get() vs get_or_404()
多表关联查询
db.session.query(User).join(Address)
User.query.join(Address)
7. 分页
方式一:使用offset 和limit
.offset(offset).limit(limit)
方式二:paginate分页支持
.paginate(page=2, per_page=4)返回Pagination的对象
Pagination对象
has_prev/has_next——是否有上一页/下一页
items——当前页的数据列表
prev_num/next_num——上一页/下一页的页码
total——总记录数
pages——总页数
8. 结合模板实现分页
第一步:准备数据
list_user = User.query.filter_by(is_valid=1)
第二步:分页
list_user.paginate(page=2, per_page=4)
第三步:在模板中实现分页操作
实战:ORM的使用
以上介绍关键代码:
flasker.py:
from flask import Flask, render_template from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # 配置数据库的连接参数 app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:woxiangni55@127.0.0.1/test_flask' db = SQLAlchemy(app) class User(db.Model): """ 用户ID """ __tablename__ = 'weibo_user' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), nullable=False) password = db.Column(db.String(255), nullable=False) birth_date = db.Column(db.Date, nullable=True) age = db.Column(db.Integer, default=0) class UserAddress(db.Model): """ 用户地址 """ __tablename__ = 'weibo_user_addr' id = db.Column(db.Integer, primary_key=True) addr = db.Column(db.String(64), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('weibo_user.id'), nullable=False) user = db.relationship('User', backref=db.backref('address', lazy=True)) @app.route('/') def index(): return render_template('index.html') @app.route('/user/<int:page>/') def page_user(page): """ 分页操作 """ page_size = 10 # 每一页10条数据 page_data = User.query.paginate(page=page, per_page=page_size) return render_template('page_user.html', page_data=page_data)
page_user.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户分页操作</title> </head> <body> <h3>总共有数据{{ page_data.total }}; 总页数{{ page_data.pages }}</h3> <ul> {% for user in page_data.items %} <li>{{ user.username }}-{{ user.password }}</li> {% endfor %} </ul> {% if page_data.has_prev %} <a href="{{ url_for('page_user', page=page_data.prev_num) }}">上一页</a> {% endif %} {% if page_data.has_next %} <a href="{{ url_for('page_user', page=page_data.next_num) }}">下一页</a> {% endif %} </body> </html>
下图数据库模型设计:
商品、分类、标签模型如下:
class Tag(db.Model): """ 商品标签 """ __tablename__ = 'product_tag' id = db.Column(db.Integer, primary_key=True) # 主键 # UID uid = db.Column(db.String(128), nullable=False, default=uuid.uuid4, unique=True) # 标签名称 name = db.Column(db.String(128), nullable=False) # 标签编码 code = db.Column(db.String(32)) # 标签的描述 desc = db.Column(db.String(256)) # 逻辑删除 is_valid = db.Column(db.Boolean, default=True) # 排序 reorder = db.Column(db.Integer, default=0) # 创建时间 created_at = db.Column(db.DateTime) # 最后修改的时间 updated_at = db.Column(db.DateTime) class Classify(db.Model): """ 商品分类 """ __tablename__ = 'product_classify' id = db.Column(db.Integer, primary_key=True) # 主键 # UID uid = db.Column(db.String(128), nullable=False, default=uuid.uuid4, unique=True) # 关联父级的ID parent_id = db.Column(db.Integer, db.ForeignKey('product_classify.id')) img = db.Column(db.String(256)) # 分类名称 name = db.Column(db.String(128), nullable=False) # 分类编码 code = db.Column(db.String(32)) # 分类的描述 desc = db.Column(db.String(256)) # 逻辑删除 is_valid = db.Column(db.Boolean, default=True) # 排序 reorder = db.Column(db.Integer, default=0) # 创建时间 created_at = db.Column(db.DateTime) # 最后修改的时间 updated_at = db.Column(db.DateTime) class Product(db.Model): """ 商品类 """ __tablename__ = 'product' id = db.Column(db.Integer, primary_key=True) # 主键 # UID uid = db.Column(db.String(128), nullable=False, default=uuid.uuid4, unique=True) # 商品标题 name = db.Column(db.String(128), nullable=False) # 商品描述(富文本) content = db.Column(db.Text, nullable=False) # 商品推荐语 desc = db.Column(db.String(256)) # 类型 types = db.Column(db.Enum('实物商品', '虚拟商品')) # 价格 price = db.Column(db.Integer, nullable=False) # 原价¥ origin_price = db.Column(db.Float) # 主图 img = db.Column(db.String(256), nullable=False) # 渠道 channel = db.Column(db.String(32)) # 链接 buy_link = db.Column(db.String(256)) # 商品状态 status = db.Column(db.Enum('销售中', '已售完', '未开始')) # 库存 sku_count = db.Column(db.Integer, default=0) # 剩余库存 remain_count = db.Column(db.Integer, default=0) # 浏览次数 view_count = db.Column(db.Integer, default=0) # 评分 score = db.Column(db.Float, default=10) # 逻辑删除 is_valid = db.Column(db.Boolean, default=True) # 排序 reorder = db.Column(db.Integer, default=0) # 创建时间 created_at = db.Column(db.DateTime) # 最后修改的时间 updated_at = db.Column(db.DateTime) class ProductClasses(db.Model): """ 商品与分类的关系 """ __tablename__ = 'product_classify_rel' id = db.Column(db.Integer, primary_key=True) # 主键 product_id = db.Column(db.Integer, db.ForeignKey('product.id')) cls_id = db.Column(db.Integer, db.ForeignKey('product_classify.id')) class ProductTags(db.Model): """ 商品与标签的关系 """ __tablename__ = 'product_tag_rel' id = db.Column(db.Integer, primary_key=True) # 主键 product_id = db.Column(db.Integer, db.ForeignKey('product.id')) tag_id = db.Column(db.Integer, db.ForeignKey('product_tag.id'))