Flask(三)

栏目:游戏资讯  时间:2023-08-01
手机版

  数据库操作

  数据库驱动(drivers)模块:pymysql、MySQLDB

  ORM 全拼,中文意为 对象-关系映射。主要实现模型对象到关系数据库数据的映射。

  ORM提供了一种面向对象操作数据库的方式给开发者。不需要编写原生SQL语句也能操作数据库,实现了业务代码与底层数据的解耦。

  优点:

  只需要面向对象编程, 不需要面向数据库编写SQL. 对数据库的操作都转化成对类/对象的属性和方法的操作. 表字段—>对象属性, SQL关键字-> 操作方法不用编写各种数据库的,当然,也可以编写原生SQL语句。 实现了数据模型代码与数据库数据的解耦, 屏蔽了不同数据库操作上的差异 不再需要关注当前项目使用的是哪种数据库。通过简单的配置就可以轻松更换数据库, 而不需要修改业务代码. 缺点:

  相比较直接使用SQL语句操作数据库,ORM需要把操作转换成SQL语句,所以有性能损失.根据对象的操作转换成SQL语句,根据查询的结果转化成模型实例对象, 在映射过程中有性能损失.不同的ORM提供的操作不一样,增加了学习成本 flask默认提供模型操作,但是并没有提供ORM,所以一般开发的时候我们会采用flask-SQLAlchemy模块来实现ORM操作。

  SQLAlchemy是一个python语言编写的高性能的关系型数据库ORM框架,它提供了高层的 ORM 和底层的原生数据库的操作。

  我们使用sqlalchemy 不需要调用sqlalchemy 本身这个模块,而是采用flask-sqlalchemy ,这是一个简化了 SQLAlchemy 操作的flask扩展模块。(主要是简化了sqlalchemy初始化代码和分页操作等)

  SQLAlchemy:https://docs.sqlalchemy.org/en/14/

  中文文档:https://www.osgeo.cn/sqlalchemy/orm/index.html

  flask-SQLAlchemy:https://flask-sqlalchemy.palletsprojects.com/en/2.x/quickstart/

  安装 flask-sqlalchemy【清华源】

  如果sqlalchemy连接的是 mysql /MariaDB数据库,需要安装 mysqldb 或pymysql驱动

  安装flask-mysqldb时,注意

  解决方案:

  原生SQLAlchemy连接配置操作

  db.py,代码:

  manage.py,代码:

  在 Flask-SQLAlchemy 中,数据库的链接配置信息使用URL指定,而且程序使用的数据库必须保存到Flask的 SQLALCHEMY_DATABASE_URI 配置项中 manage.py,代码:

  其他设置项:

  配置完成需要去 MySQL 中创建项目所使用的数据库 模型字段类型名python中数据类型说明Integerint普通整数,一般是32位SmallIntegerint取值范围小的整数,一般是16位BigIntegerint不限制精度的整数Floatfloat浮点数Numericdecimal.Decimal普通数值,一般是32位Stringstr变长字符串Textstr变长字符串,对较长或不限长度的字符串做了优化Unicodeunicode变长Unicode字符串UnicodeTextunicode变长Unicode字符串,对较长或不限长度的字符串做了优化Booleanbool布尔值DateTimedatetime.datetime日期和时间Datedatetime.date日期Timedatetime.time时间LargeBinarybytes二进制文件内容Enumenum.Enum枚举类型,相当于django的choices,但是功能没有choices那么强大 选项名说明primary_key如果为True,代表当前数据表的主键unique如果为True,为这列创建唯一 索引,代表这列不允许出现重复的值index如果为True,为这列创建普通索引,提高查询效率nullable如果为True,允许有空值,如果为False,不允许有空值default为这列定义默认值 在SQLAlchemy中,添加、修改、删除操作,均由数据库会话(sessionSM)管理。 会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 db.commit() 方法提交会话。 在SQLAlchemy 中,查询操作是通过 query 对象操作数据。 最基本的查询是返回表中所有数据,也可以通过filter过滤器进行更精确的数据库查询。 我们后面会把模型创建到单独的文件中,但是现在我们先把模型类写在main.py文件中。

  创建表

  删除表

  代码:

  添加一条数据

  一次插入多条数据

  删除数据

  更新数据

  过滤器说明filter()把过滤器添加到原查询上,返回一个新查询filter_by()把等值过滤器添加到原查询上,返回一个新查询limit()使用指定的值限定原查询返回的结果数量offset()设置结果范围的开始位置,偏移原查询返回的结果,返回一个新查询order_by()根据指定条件对原查询结果进行排序,返回一个新查询group_by()根据指定条件对原查询结果进行分组,返回一个新查询 方法说明all()以列表形式返回查询的所有结果first()返回查询的第一个结果,模型对象,如果未查到,返回Nonefirst_or_404()返回查询的第一个结果,模型对象,如果未查到,通过abort 返回404异常get()返回指定主键对应的模型对象,如不存在,返回Noneget_or_404()返回指定主键对应的行,如不存在,abort 返回404count()返回查询结果的数量paginate()返回一个Paginate分页器对象,它包含指定范围内的结果having()返回结果中符合条件的数据,必须跟在group by后面,其他地方无法使用。 get():参数为主键,表示根据主键查询数据,如果主键不存在返回None

  课堂代码:

  all()返回查询到的所有对象

  count 返回结果的数量

  first()返回查询到的第一个对象【first获取一条数据,all获取多条数据】

  filter条件查询,支持各种运算符和查询方法或者模糊查询方法。

  返回名字结尾字符为g的所有数据。

  filter_by精确条件查询

  filter_by 只支持字段的值是否相等的情况,对于大于、等于、等等其他条件是不支持的。

  例如:返回age等于22的学生

  练习:

  代码:

  逻辑与,需要导入,返回条件满足的所有数据

  逻辑或,需要导入or_

  逻辑非,返回名字不等于"小白"的所有数据

  not_ 相当于取反

  in_范围查询

  is_判断值查询

  order_by 排序

  count统计

  对结果进行偏移量和数量的限制

  SQL

  练习

  manage.py,代码:

  list.html,代码:

  分组查询和分组查询结果过滤

  一般分组都会结合聚合函数来一起使用。SQLAlchemy中所有的聚合函数都在模块中声明的。

  函数名说明func.count统计总数func.avg平均值func.min最小值func.max最大值func.sum求和 代码:

  SQL方法中的关键字顺序:

  选项名说明backref在关系的另一模型中添加反向引用,用于设置外键名称,在1查多的primary join明确指定两个模型之间使用的连表条件, 用于1对1 或者1对多连表中lazy指定如何加载关联模型数据的方式,用于1对1或1对多链表中。参数值:

  select(立即加载,查询所有相关数据显示,相当于lazy=True)

  subquery(立即加载,但使用子查询)

  dynamic(不立即加载,但提供加载记录的查询对象)uselist指定1对1或1对多连表时,返回的数据结果是模型对象还是模型列表,如果为False,不使用列表,而使用模型对象。

  1对1或多对1关系中,需要设置relationship中的uselist=Flase,1对多或多对多关系中,需要设置relationshio中的uselist=True。secondary指定多对多关系中关系表的名字。

  多对多关系中,需建立关系表,设置 secondary=关系表secondary join在SQLAlchemy中无法自行决定时,指定多对多关系中的二级连表条件,绑定主外键。 范式理论:一套提供给数据库开发者设置标准、规范的数据库的理论。

  三范式+逆范式

  第三范式:数据不能冗余,把关联性不强的数据可以移除到另一个表中。使用外键进行管理。

  一对一 常见的业务:主表和详情表(用户、会员、学生、商品、文章、主机)

  关联属性声明在主模型中【最常用】 代码:

  在外键模型中声明关联属性 代码:

  一对多 常见业务:商品分类和商品、文章分类和文章、班级与学生、部门与员工、角色与会员、订单与订单详情、用户与收货地址。。。

  其中realtionship描述了Student和StudentAddress的关系。第1个参数为对应参照的类"StudentAddress"第3个参数backref为类StudentAddress声明关联属性第4个参数lazy决定了什么时候SQLALchemy什么时候执行读取关联模型的SQL语句 lazy=‘subquery’,查询当前数据模型时,采用子查询(subquery),把外键模型的属性也同时查询出来了。lazy=True或lazy=‘select’,查询当前数据模型时,不会把外键模型的数据查询出来,只有操作到外键关联属性时,才进行连表查询数据[执行SQL]lazy=‘dynamic’,查询当前数据模型时,不会把外键模型的数据立刻查询出来,只有操作到外键关联属性并操作外键模型具体字段时,才进行连表查询数据[执行SQL] 常用的lazy选项:dynamic和select 课堂代码:

  manage.py,代码:

  多对多 常见业务:用户收藏文章、用户与用户之间的好友关系、点赞、评论、关注、用户浏览商品的历史记录、订阅文章、专题与商品/文章的关系、活动与商品。。。。。

  基于第三方关系表构建多对多 代码:

  多对多,也可以拆解成3个模型(2个主模型,1个关系模型,关系模型保存了2个主模型的外键),其中tb_achievement作为单独模型存在。

  基于第三方关系模型构建多对多 在SQLAlchemy中,基于db.Table创建的关系表,如果需要新增除了外键以外其他字段,无法操作。所以将来实现多对多的时候,除了上面db.Table方案以外,还可以把关系表声明成模型的方法,如果声明成模型,则原来课程和学生之间的多对多的关系,就会变成远程的1对多了。

  代码:

  relationship还有一个设置外键级联的属性:cascade=“all, delete, delete-orphan”

  也叫虚拟外键。主要就是在开发中为了减少数据库的性能消耗,提升系统运行效率,一般项目中如果单表数据太大[千万级别]就不会使用数据库本身维护的物理外键,而是采用由ORM或者我们逻辑代码进行查询关联的逻辑外键。

  SQLAlchemy设置外键模型的虚拟外键,有2种方案:

  方案1,查询数据时临时指定逻辑外键的映射关系:

  方案2,在模型声明时指定逻辑外键的映射关系(最常用,这种设置方案,在操作模型时与原来默认设置的物理外键的关联操作是一模一样的写法):

  例1,虚拟外键使用的方案1,代码:

  例2,虚拟外键使用的方案2,代码:

  在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据,所以往往更常见的方式就是使用alter来改变数据结构,原有数据中的新字段值设置默认值或null=True.更好的解决办法是使用数据迁移,它可以追踪数据库表结构的变化,然后把变动应用到数据库中。在Flask中可以使用Flask-Migrate的第三方扩展,来实现数据迁移。并且集成到Flask终端脚本中,所有操作通过flask db 命令就能完成。为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask框架中。 首先要在虚拟环境中安装Flask-Migrate。

  官网地址:https://flask-migrate.readthedocs.io/en/latest/

  代码文件内容:

  自动创建迁移版本文件中有两个函数,用于进行数据迁移同步到数据库操作的。 upgrade():把迁移中的改动代码同步到数据库中。downgrade():则将改动代码从数据库中进行还原。 自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。生成的迁移文件不一定完全正确,有可能代码中存在细节遗漏导致报错,需要开发者进行检查,特别在多对多的时候 把当前ORM模型中的代码改动同步到数据库。

  可以根据history命令找到版本号,然后传给downgrade命令:

  数据迁移的步骤:

  文档: https://faker.readthedocs.io/en/master/locales/zh_CN.html

  批量生成测试数据: https://github.com/joke2k/faker

  代码:

  flask-session,允许设置session到指定的存储空间中,例如:redis/mongoDB/mysql。

  文档: https://flask-session.readthedocs.io/en/latest/

  使用session之前,必须配置一下配置项:

  需要手动创建session表,在项目第一次启动的时候,使用来完成创建。

  这个功能必须确保,服务器必须已经安装了redis而且当前项目虚拟环境中已经安装了redis扩展库

  flask-redis是第三方开发者为了方便我们在flask框架中集成redis数据库操作所封装一个redis操作库、

  在flask中要基于flask-redis进行数据库则可以完成以下3个步骤即可:

  在redis中保存session,代码:

  随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过django的子应用管理,flask程序进行可以进行类似的模块化处理保存代码。

  简单来说,Blueprint 是一个存储视图方法/模型代码的容器(目录),这些操作在这个Blueprint 被注册到flask的APP实例对象应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理客户端请求的视图。

  Flask使用Blueprint让应用实现模块化,在Flask中Blueprint具有如下属性:

  一个项目可以具有多个Blueprint可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名,也就是说每一个蓝图都可以像django那样有属于自己的路由前缀在一个flask项目中,同一个BluePrint模块可以注册多次,也就是说一个蓝图可以对应多个不同的url地址。Blueprint目录可以保存单独属于自己的模板目录保存自己的模板文件、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的在一个flask项目初始化时,就应该要注册需要使用的Blueprint,否则项目不识别Blueprint蓝图 注意:flask中的Blueprint并不是一个完整的项目应用,它不能独立运行,而必须要把蓝图blueprint注册到某一个flask项目中才能使用。

  在flask中,要使用蓝图Blueprint可以分为四个步骤:

  手动创建一个蓝图的包目录,例如users,并在文件中创建蓝图实例对象users_blueprint 在这个users蓝图目录下创建蓝图的子文件, 其中我们可以创建views.py文件,保存当前蓝图使用的视图函数 在中引入views.py中所有的视图函数并绑定路由 在主应用下程序入口manage.py文件中把这个users_blueprint蓝图对象注册app实例对象中,运行起来。 当这个应用启动后,通过/users/可以访问到蓝图中定义的视图函数

  蓝图Blueprint实际上的作用就是,充当当前蓝图目录下的所有视图和url路由地址的绑定关系的临时容器在视图函数被蓝图的add_url_rule方法注册时,这个操作本质就是将视图和url地址的映射关系添加到蓝图的子路由列表deferred_functions中。蓝图对象根本没有路由表,当我们在蓝图中的视图函数上调用route装饰器(或者add_url_role函数)注册路由时,它只是在蓝图对象的内部的deferred_functions(子路由列表)中添加了一个路由项(路由项实际上就是一个绑定了视图和url地址的lambda匿名函数)当执行app.register_blueprint()注册蓝图时,app应用实例对象会将从蓝图对象的 deferred_functions列表中循环取出每一个之前注册的路由项,并把app应用实例对象自己作为参数执行路由项对应的lambda匿名函数,lambda匿名函数执行以后就会调用app.add_url_rule() 方法,这就将蓝图下子路由列表之前暂存的路由全部添加到了app应用实例对象的url_map总路由表中了,所以用户就可以在flask中访问到了蓝图中的视图。当然,能访问蓝图下的视图,自然也就可以通过视图调用其他的功能,例如:蓝图下的其他功能函数或其他的模型对象了。 当我们在app应用实例对象上注册一个蓝图时,可以指定一个url_prefix关键字参数(这个参数默认是/)

  [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xMLbsRDR-1689495494525)(assets/image-20220105173129138.png)]

  在app应用实例对象的最终的路由表 url_map中,在蓝图上注册的路由URL自动被加上了这个路由前缀,这个可以保证在多个蓝图中使用相同的子路由而不会最终引起冲突,只要在注册蓝图时将不同的蓝图挂接到不同的自路径即可。

  注意:有了蓝图以后,在flask使用url_for在使用时,如果要生成一个蓝图里面的视图对应的路由地址,则需要声明当前蓝图名称+视图名称

  users/views.py,代码:

  访问:

  [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-af06bKju-1689495494526)(assets/image-20220105173329978.png)]

  和app应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时手动指定 static_folder 参数。

  下面的代码将蓝图所在目录下的static_users目录设置为静态目录

  ,代码:

  [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YcNVWjGk-1689495494527)(assets/image-20220105180218123.png)]

  现在就可以使用http://127.0.0.1:5000/users/static/images/avatar.jpg 访问users/static/目录下的静态文件了.

  当然,也可以修改访问静态文件的路径 :可以在创建蓝图对象时使用 static_url_path 来改变静态目录的url地址。

  现在就可以使用http://127.0.0.1:5000/users/static/assets/avatar.jpg访问users/static/目录下的静态文件了.

  [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-paq34Nd0-1689495494527)(assets/image-20220105180417738.png)]

  创建蓝图中的模板目录templates,,代码:

  视图,代码:

  模板代码,,代码:

  [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXZ7o2mf-1689495494527)(assets/image-20220105181038194.png)]

  注意:如果公司使用了flask1.x版本,则不能出现项目根目录下和蓝图目录下2个templates目录的情况,否则项目根目录下的templates模板会覆盖蓝图目录下的同名模板,flask会优先加载项目根目录下的模板。

上一篇:星光大道
下一篇:骨骼小知识