Flask 第四课Flask数据模型Model
tags:
- Flask
- 2019千锋教育
categories:
- flask
- model
- 数据模型
- flask-caching
文章目录
第一节 flask的模型
Flask默认并没有提供任何数据库操作的API,我们可以选择任何适合自己项目的数据库来使用Flask中可以自己的选择数据,用原生语句实现功能,也可以选择ORM ( SQLAIchemy,MongoEngine)
- 原生SQL缺点
- 代码利用率低,条件复杂代码语句越长,有很多相似语句
- 一些SQL是在业务逻辑中拼出来的,修改需要了解业务逻辑
- 直接写SQL容易忽视SQL问题
- ORM将对对象的操作转换为原生SQL优点
- 性能损耗少
- 设计灵活,可以轻松实现复杂查询
- 移植性好
python的ORM (SQLAIchemy)针对于Flask的支持: pip install flask-sqlalchemy -i https://pypi.douban.com/simple
第二节 数据库连接
1. mysql连接
dialect+driver://usermame:password@host:port/database
dialect数据库实现 driver数据库的驱动
例子:
app.config[‘SQLALCHEMY_DATABASE_URI’]=‘mysql+pymysql://root:[email protected]:3306/flask_test’
2. Sqlite连接
app.config[‘SQLALCHEMY_ DATABASE_ URI’]= DB URI
aDD.configI’SQLALCHEMY_ TRAKE_ MODIFICATIONS’]=False 禁止对象追踪修改
SQLite数据库连接不需要额外驱动,也不需要用户名和密码
SQLite连接的URI:DB_ URI = sglite:///sqlite3.db
第三节 ORM字段类型
常用字段: | Smalllnteger | BigInteger | Float |
---|---|---|---|
Numeric | String | Text | Unicode |
Unicode Text | Boolean | Date | Time |
DateTime | Interval | LargeBinary |
- 初始化:python manage.py db init
- 升级:python manage.py db upgrade
- 降级回退:python manage.py db downgrade
- 生成迁移文件:python manage.py db migrate
- 生成迁移数据库前要看一下模型类,有没有注册到views文件中。from App.models import models, User
- models.create_all()创建表, models.drop_all()删除表
- 常用约束
- primary key autoincrement unique index
- nullable default ForeignKey()
1. 继承自己的定义的模型类
- 表名: _tablename
- 模型继承
- 默认继承并不会报错,它会将多个模型的数据映射到一张表中,导致数据混乱,不能满足基本使用
- 抽象的模型是不会在数据库中产性映射的: abstract =True
- 文档
- flask-sqlalchemy
- sqlalchemy
- 实际案例
# 会员数据模型
# 修改完模型后:python manage.py db migrate
# 修改完模型后:python manage.py db upgrade
class User(models.Model):
# 修改表名
__tablename__ = 'UserModel'
id = models.Column(models.Integer, primary_key=True) # 编号
username = models.Column(models.String(100), unique=True) # 昵称
password = models.Column(models.String(100)) # 密码
desc = models.Column(models.String(128), nullable=True)
- 如果继承类,只生成一张Animal表。不生成dog和cat表。dog和cat的字段集成在Animal表中。如果对cat和dog表添加数据。一些字段会默认赋值,数据就混乱了。解决方法见6.
# 如果继承类,只生成一张Animal表。不生成dog和cat表。dog和cat的字段集成在Animal表中。
class Animal(models.Model):
id = models.Column(models.Integer, primary_key=True)
u_name = models.Column(models.String(100), unique=True)
class Dog(Animal):
d_tags = models.Column(models.Integer, default=4)
class Cat(Animal):
c_tags = models.Column(models.String(32), default='fish')
- 降级表,把Animal变成抽象表。然后重新生成表结构。发现生成dog表和cat表
# 降级表:1. python manage.py db downgrade
# 降级表:2. 删除对应的表py文件
# 降级表:3. python manage.py db migrate
# 降级表:4. python manage.py db upgrade
class Animal(models.Model):
# Django 中可以把类变成抽象类处理,这里也把类变为抽象类
__abstract__= True
id = models.Column(models.Integer, primary_key=True)
u_name = models.Column(models.String(100), unique=True)
class Dog(Animal):
d_tags = models.Column(models.Integer, default=4)
class Cat(Animal):
c_tags = models.Column(models.String(32), default='fish')
2. 项目中数据库优化
- 怎么优化
- 连接多少次(mysql默认最大连接数目是100个)
- 其实无论是Django还是flask都是有数据库连接池的。
- SQLALCHEMY_POOL_SIZE (连接池大小默认5个)
- SQLALCHEMY_POOL_TIMEOUT 超时时间
- SQLALCHEMY_POOL_RECYCLE (回收时间默认两小时)
第四节 ORM数据操作
1. 添加数据
- 下面导入的models = SQLAlchemy(),也常常把models变为db。
- add()添加,add_all()添加多条数据
# 插入单条数据
@blue.route('/adduser')
def adduser():
user = User()
user.username = 'root'
user.password = '123456'
models.session.add(user)
models.session.commit()
print(models.session)
print(type(models.session))
return '添加成功'
@blue.route('/addusers')
def addusers():
users = []
for i in range(5):
user = User()
user.username = 'bili%d'% i
user.password = '123456'
users.append(user)
models.session.add_all(users)
models.session.commit()
return '添加多条成功'
2. 查询数据
2.1 普通查询
- 查询数据结果集
- 语法: 类名.guery.xxx
- 获取单个对象
- first() 或 first_or_404()
- get() 或 get_or_404() 只支持id不支持其他字段
- 获取结果集
- all() 比较特殊。返回一个列表
- filter(类名.属性名.运算符(xxx’)) 返回BaseQuery对象。
- filter(类名.属性数学运算符值)
- 魔术方法:eq lt gt ge le
- 操运算作符: == > <
- contains startswith endswith in_ like
- filter_by() offset()偏移 limit() order_ by() paginate()
- offset和limit不区分顺序,怎么写都按offset先跳过在限制
- order_by必须在offset和limit之前
- BaseQuery对象后可以继续加条件,但是list后不可以
- 所以如果在flask-sqlalchemy使用list, 要放在最后。
@blue.route('/getcat')
def getcat():
#cats = Cat.query.filter(Cat.id.__gt__(2)).all()
#cats = Cat.query.filter(Cat.id == (5)).all()
#cats = Cat.query.filter(Cat.u_name.contains('猫')).all()
#cats = Cat.query.filter(Cat.id.in_([1, 5, 6])).all()
#cats = Cat.query.order_by(Cat.id.desc()).all()
# offset和limit不区分顺序,怎么写都按offset先跳过在限制
#cats = Cat.query.offset(1).limit(2).all()
#cats = Cat.query.order_by("id").offset(1).limit(2).all()
# BaseQuery的__str__方法, 会把它转为sql语句
#cats = Cat.query.order_by("id").offset(1).limit(2).all()
cats = Cat.query.filter_by(id=5).all()
print(cats)
print(type(cats))
#render_template('cat.html', cat=cats)
return "成功"
@blue.route('/getdog')
def getdog():
from flask import request
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 4, type=int)
dogs = Dog.query.offset(per_page * (page - 1)).limit(per_page)
print(dogs)
print(type(dogs))
return "成功"
@blue.route('/getdogwithpage/')
def getdogwithpage():
#dogs = Dog.query.paginate().items
#render_template('dog.html', dog=cdogs)
# 传分页器过去而不是数据过去
pagination = Dog.query.paginate()
render_template('dog.html', pagination=pagination)
2.2 外键关联
- filter_by 通常用在级联数据查询上。直接手动获取如下:
# 创建模型
class Customer(models.Model):
id = models.Column(models.Integer, primary_key=True, autoincrement=True)
c_name = models.Column(models.String(16))
class Address(models.Model):
id = models.Column(models.Integer, primary_key=True, autoincrement=True)
a_position = models.Column(models.String(20))
a_customer_id = models.Column(models.Integer, models.ForeignKey(Customer.id))
@blue.route('/addcustomer')
def add_customer():
import random
costumer = Customer()
costumer.c_name = '剁手党%d' % random.randrange(1000)
models.session.add(costumer)
models.session.commit()
return "添加成功"
@blue.route('/addaddress')
def add_address():
import random
address = Address()
address.a_position = '上海%d' % random.randrange(1000)
address.a_customer_id = Customer.query.order_by(Customer.id.desc()).first().id
models.session.add(address)
models.session.commit()
return "添加成功"
@blue.route('/get_customer')
def get_customer():
# 获取id=1的地址的客户
address = Address.query.get_or_404(1)
customer = Customer.query.get(address.a_customer_id)
return customer.c_name
@blue.route('/get_addr/')
def get_addr():
# 客户1的所有地址
customer = Customer.query.get(1)
addresses = Address.query.filter_by(a_customer_id=customer.id)
for addr in addresses:
print(addr.a_position)
return '获取成功'
- 使用关系Relationships获取: relationship 通过customer直接获取得到一个列表(address = models.relationship(‘Address’, backref=‘customer’, lazy=True))
class Customer(models.Model):
id = models.Column(models.Integer, primary_key=True, autoincrement=True)
c_name = models.Column(models.String(16))
address = models.relationship('Address', backref='customer', lazy=True)
class Address(models.Model):
id = models.Column(models.Integer, primary_key=True, autoincrement=True)
a_position = models.Column(models.String(20))
a_customer_id = models.Column(models.Integer, models.ForeignKey(Customer.id))
@blue.route('/get_addr/')
def get_addr():
# 客户1的所有地址
customer = Customer.query.get(1)
#addresses = Address.query.filter_by(a_customer_id=customer.id)
# relationship 通过customer直接获取得到一个列表
addresses = customer.address
print(addresses)
for addr in addresses:
print(addr.a_position)
return '获取成功'
- 关系
- 一对一:ForeignKey+Unique
- 一对多:ForeignKey
- M*N多对多:额外关系表 ForeignKey+ForeignKey
- 一对一和多对多 项目中不建议使用。自己手动维护官网也有生成中间表的写法。
2.3 逻辑运算
- 与 and_ filter(and_ (条件),条件…)
- 或 or_ filter(or_ (条件),条件…)
- 非 not_ filter(not_ (条件),条件…)
@blue.route('/get_addrwithconn/')
def get_addrwithconn():
from sqlalchemy import and_,or_,not_
#addresses = Address.query.filter(Address.a_customer_id.__eq__(1)).filter(Address.a_position.endswith('5'))
# 逻辑符号and_
#addresses = Address.query.filter(and_(Address.a_customer_id.__eq__(1), Address.a_position.endswith('5')))
# 逻辑符号or_
#addresses = Address.query.filter(or_(Address.a_customer_id.__eq__(1), Address.a_position.endswith('5')))
# 逻辑符号not_
#addresses = Address.query.filter(not_(Address.a_customer_id.__eq__(1)))
addresses = Address.query.filter(not_(or_(Address.a_customer_id.__eq__(1), Address.a_position.endswith('5'))))
print(addresses)
for addr in addresses:
print(addr.a_position)
#return render_template('Address.hmtl', addresses=addresses)
return '获取成功'
3. 删除修改数据
- 删除数据。delete
- models.session.delete() models.session.commit()
- 修改数据。add重新保存
@blue.route('/deleteuser')
def deleteuser():
user = User.query.first()
models.session.delete(user)
models.session.commit()
return '删除数据成功'
@blue.route('/updateuser')
def updateuser():
user = User.query.first()
user.username = 'root'
# 修改数据后重新保存
models.session.add(user)
models.session.commit()
return '修改数据成功'
第五节 缓存查询flask-caching
- 低版本的flask用flask-cache
- 高版本的flask用flask-caching(功能一样,都是做数据缓存的。重复请求从缓存中读数据).说明文档可能被墙。
- pip install flask-caching
- 导包from flask_caching import Cache
- 配置:可以配置null, simple, redis, memcached,filesystem
cache = Cache(config={
"CACHE_TYPE": "simple"
})
- 注册cache.init_app(app=app)
- 使用如下
@blue.route('/get_addrwithconn/')
@cache.cached(timeout=60)
def get_addrwithconn():
from sqlalchemy import not_, or_
addresses = Address.query.filter(not_(or_(Address.a_customer_id.__eq__(1), Address.a_position.endswith('5'))))
print(addresses)
print('从数据库中查询')
for addr in addresses:
print(addr.a_position)
#return render_template('Address.hmtl', addresses=addresses)
return '获取成功'