Flask中数据库关联表与缓存cache(十二)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 1 一对多(One To Many)表示一对多的关系时,在子表类 Post 中需要通过 foreign key (外键)引用父表类 User在Post类中指定ForeignKey:class Post(db.

1 一对多(One To Many)

表示一对多的关系时,在子表类 Post 中需要通过 foreign key (外键)引用父表类 User

在Post类中指定ForeignKey:

class Post(db.Model):
    __tablename__ = 'posts'
    id = db.Column(db.String(45), primary_key=True)
    title = db.Column(db.String(255))
    text = db.Column(db.Text())
    publish_date = db.Column(db.DateTime)
    # 设置外键ForeignKey
    user_id = db.Column(db.String(45),db.ForeignKey('users.id'))

user_id 字段是 posts 表的外键,代表了外键约束。强制规定了字段 user_id 的值必须同时存在于User.id 列中。用来保证每一篇post都能对应找到一个user,而且一个user能够对应多篇 posts

在User父类指定

class User(db.Model):
    # 设置表名为users
    __tablename__ = 'users'
    id = db.Column(db.String(45), primary_key=True)
    username = db.Column(db.String(255))
    password = db.Column(db.String(255))
    # 设置连接posts的并反向查询users
    posts = db.relationship('Post',backref='users',lazy='dynamic')
#一对多
#Post设置外键的表,属于多
user_id = db.Column(db.String(45), db.ForeignKey('users.id'))
#User设置关联的表,属于1
posts = db.relationship('Post',backref='users',lazy='dynamic')

注意:

  1. db.relationsformat(self.username): 会在 SQLAlchemy 中创建一个虚拟的列,该列会与 Post.user_id (db.ForeignKey) 建立联系

  2. backref:用于指定表之间的双向关系,如果在一对多的关系中建立双向的关系,这样的话在对方看来这就是一个多对一的关系,反向查询的表名

  3. lazy:指定 SQLAlchemy 加载关联对象的方式。
    ​ lazy=subquery: 会在加载 Post 对象后,将与 Post 相关联的对象全部加载
    ​ lazy=dynamic: 只有被使用时,对象才会被加载,并且返回式会进行过滤

2 多对多(Many To Many)

class Posts(db.Model):
    __tablename__ = 'posts'
    id = db.Column(db.Integer,primary_key=True)
    content = db.Column(db.Text)
    pid = db.Column(db.Integer,default=0)
    path = db.Column(db.String(255),default='0,')
    timestamp = db.Column(db.DateTime,default=datetime.utcnow)
    #一对多posts为多,设置外键 Foreignkey
    uid = db.Column(db.Integer,db.ForeignKey('user.id'))

class User(UserMixin,db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(12),index=True)
    password_hash = db.Column(db.String(128))
    sex = db.Column(db.Boolean,default=True)
    age = db.Column(db.Integer)
    email = db.Column(db.String(40))
    icon = db.Column(db.String(70),default='default.jpg')
    #当期账户激活状态
    confirm = db.Column(db.Boolean,default=False)
    #参数1模型名称   参数2反向引用的字段名   参数3 加载方式 提供对象
    #post与user为一对多,一设置反向查询表名user
    posts = db.relationship('posts',backref='user',lazy='dynamic')
    #post与collect是多对多关系
    favorite = db.relationship('Posts',secondary='collections',backref=db.backref('users',lazy='dynamic'),lazy='dynamic')

    #中间表collections,多对多
collections = db.Table('collections',
    db.Column('user_id',db.Integer,db.ForeignKey('user.id')),
    db.Column('posts_id',db.Integer,db.ForeignKey('posts.id'))
)

关于中间表

也可以写成类的形式创建
class Collections(db.Model):
    __tablename__ = 'collectionss'
    user_id = db.Column('user_id', db.Integer, db.ForeignKey('user.id'))
    posts_id = db.Column('posts_id', db.Integer, db.ForeignKey('posts.id'))

注意:

  • many to many 的关系仍然是由 db.relationship() 来定义
  • seconddary(次级):会告知 SQLAlchemy 该 many to many 的关联保存在 posts_tags 表中
  • backref:声明表之间的关系是双向,帮助手册 help(db.backref)。需要注意的是:在 one to many 中的 backref 是一个普通的对象,而在 many to many 中的 backref 是一个 List 对象。

3 数据迁移

from flask_migrate import Migrate
migrate = Migrate(main.app, models.db)   #绑定app与db
manager = Manager(main.app)
manager.add_command("db", MigrateCommand)  #给数据库迁移重命名db
python3 manage.py db init   #初始化 DB Migrate
python3 manage.py db migrate   #开始迁移生成migrations 目录,保存所有的更改记录文件
python3 manage.py db upgrade   #将需要修改的数据库结构的更新内容,手动的写入到记录文件中,然后执行                                   upgrade 指令就能够实现数据库的更新

python manage.py db history    # 获取 History ID
python manage.py db downgrade <history_id>  # 回滚到某个 history

4 paginate分页类

(1) paginate 分页类

返回 pagination分页对象
参数:
    page 必须参数  代表当前的页码
    per_page    每页显示数据的条数  默认为20条
    error_out   当分页查询出现异常的时候 是否抛出错误 默认抛出 改为Flase

(2) pagination分页对象 属性

items   当前分页的所有数据
page    当期页码
pages   总页码数
total   总记录数
per_page    每页显示数据的条数
prev_num    返回上一页的页码
next_num    返回下一页的页码
has_prev    是否存在上一页
has_next    是否存在下一页

(3) pagination分页对象 方法

prev          上一页的分页对象
next          下一页的分页对象
iter_pages     是一个迭代器  返回分页栏上的页码数 如果显示不下 返回None

5 flask-cache 缓存

安装

pip3 install flask-cache

导入使用

#自带simple缓存
from flask_cache import Cache
cache = Cache(app,config={'CACHE_TYPE': 'simple'})  #实例化cache

或者利用工厂函数
cache = Cache(config={'CACHE_TYPE': 'simple'})
def create_app(app):   #工厂函数
    cache.init_app(app)

#注: simple代表简单的缓存,容易出现问题,一般可以采用redis代替
cache = Cache(app, config={'CACHE_TYPE': 'redis',          # Use Redis
                           'CACHE_REDIS_HOST': 'abc.com',  # Host, default 'localhost'
                           'CACHE_REDIS_PORT': 6379,       # Port, default 6379
                           'CACHE_REDIS_PASSWORD': '111',  # Password
                           'CACHE_REDIS_DB': 2}            # DB, default 0

缓存无参数的普通函数 : @ cache.cached(timeout,key_prefix)

@cache.cached(timeout=100,key_prefix='index')

#NOTE 1: 装饰器 @ cache.cached() 的形参数 key_prefix 是必须的, 并且传入的值是唯一的, 这样 Flask-Cache 才能够正确的返回该函数缓存的值.

#NOTE 2: 该函数被缓存的时间设定为100s, 这是因为该函数返回的值不被经常改变, 而且就算是被改变了也不会对整体页面的显示有大的影响.

缓存带参数的普通函数 : @cache.memoize(timeout)

不带参数的装饰器不考虑参数问题,就算函数接受不同的实参还是返回相同的结果,这明显的不科学的.因此采用记忆型的 @cache.memoize(),但是使用与纯函数调用

@cache.memoize(timeout=100)

#NOTE 1: 装饰器 @cache.memoize() 不仅仅会缓存运行的结果, 还缓存调用时的参数, 所以在函数接受到相同的参数时, 就会将缓存中该参数对应的结果返回.

#NOTE 2: @cache.momoize() 非常不适用于返回结果依赖全局变量或依赖数据库查询的函数, 因为在这很容易导致数据竞态, 即输入了相同的参数, 可能会返回不同的结果. 最适合被缓存的函数是纯函数(在传入的参数相同的情况下会输出相同的结果)

缓存无动态参数的视图函数

@blog_blueprint.route('/<int:page>')
@cache.cached(timeout=60)

#NOTE 1: 这里仍然是使用装饰器 @cache.cached(), 但却不必需要指定 key_prefix 形参. 
#NOTE 2: timeout 表示该视图函数返回的结果将会被缓存的时长.

缓存带动态参数的视图函数

@blog_blueprint.route('/post/<string:post_id>', methods=('GET', 'POST'))
@cache.cached(timeout=60, key_prefix=make_cache_key)

#NOTE 1: 这里我们仍然使用了 key_prefix 参数来进行定位, 而且该形参除了可以接受 String 类型对象之后, 也可以接收一个函数.

#NOTE 2: 函数 make_cache_key() 会动态的生成一个唯一的缓存键, 用于确定这一次请求的 URL 是否存在缓存, 如果存在则返回, 如果不存在则新建. 所以在该函数中我们使用了请求 path + hash 字符串结合而成的唯一字符串作为缓存键.

删除缓存

(1) 给settings.py 设置参数
    CACHE_DEFAULT_TIMEOUT

(2) 给装饰器 添加 timeout参数
    @cache.cached(timeout=100)

(3) 清除所有的缓存
    cache.clear()

(4) 清除cached的缓存
    @cache.cached(timeout=100,key_prefix='index') #当前缓存的前缀
    cache.delete('index')

(5) 清除 memoize的缓存
    cache.delete_memoized(视图函数的名称)
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
22天前
|
缓存 NoSQL 关系型数据库
在Python Web开发过程中:数据库与缓存,MySQL和NoSQL数据库的主要差异是什么?
MySQL是关系型DB,依赖预定义的表格结构,适合结构化数据和复杂查询,但扩展性有限。NoSQL提供灵活的非结构化数据存储(如JSON),无统一查询语言,但能横向扩展,适用于大规模、高并发场景。选择取决于应用需求和扩展策略。
112 1
|
1月前
|
缓存 算法 Java
Caffeine Cache~高性能 Java 本地缓存之王
Caffeine Cache~高性能 Java 本地缓存之王
51 1
|
1月前
|
关系型数据库 API 数据库
盘点Flask与数据库的交互插件——Flask-Sqlalchemy
盘点Flask与数据库的交互插件——Flask-Sqlalchemy
26 0
|
1月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
3月前
|
缓存 NoSQL Java
Spring Cache 缓存原理与 Redis 实践
Spring Cache 缓存原理与 Redis 实践
142 0
|
3月前
|
存储 缓存 NoSQL
把ruoyi-vue前后端分离项目的redis缓存改为本地缓存cache
把ruoyi-vue前后端分离项目的redis缓存改为本地缓存cache
148 0
|
21天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
44 0
|
3月前
|
前端开发 数据库 Python
使用 Python 的 Web 框架(如 Django 或 Flask)来建立后端接口,用于处理用户的请求,从数据库中查找答案并返回给前端界面
【1月更文挑战第13天】使用 Python 的 Web 框架(如 Django 或 Flask)来建立后端接口,用于处理用户的请求,从数据库中查找答案并返回给前端界面
80 7
|
1月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
45 1
|
1月前
|
缓存 NoSQL 数据库
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
[Redis]——数据一致性,先操作数据库,还是先更新缓存?