《Flask Web开发实战:入门、进阶与原理解析》 学习笔记 好书呀
dynamic 选项仅用于集合关系属性,不可用于多对一 、 一 对一或是在关系函数中将uselist 参数设为 False 的情况
![](https://img2022.cnblogs.com/blog/1161222/202205/1161222-20220509180155873-580842173.png)
多教程和示例使用 dynamic动态加载所有集合关系属性对应的记录,这是应该避免
的行为 。 使用 dynamic 加载方式意味着每次操作关系都会执行一 次 SQL 查询,这会造成
潜在的性能问题 。 大多数情况下我们只需妥使用默认值( select),只有在调用关系属性
会返回大量记录,并且总是需要对关系属性返回的结果附加额外的查询时才需要使用动
态加载( lazy=' dynamic’ ) 。
因为外键只能存储单一数据(标量)所以外键总是在“多”这一侧定义,
关系属性在关系的出发侧定义,即一对多关系 的“一”这一侧 。
db.relationship() 的第一个参数表明这个关系的另一端是哪个模型。如果关联的
模型类在模块后面定义,可使用字符串形式指定
调用 db.relationship() 时要把 uselist 设为 False,把“多”变成“一”。 多对一
关系也可使用一对多表示,对调两个表即可,或者把外键和 db.relationship() 都放在
“多”这一侧。最复杂的关系类型是多对多,需要用到第三张表,这个表称为关联表(或
联结表)。
新建对象时没有明确设定 id 属性,因为在多数数据库中主键由数据库自身管理。现
数据库会话也可回滚。调用 db.session.rollback() 后,添加到数据库会话
中的所有对象都将还原到它们在数据库中的状态。
如果使用 errorhandler 装饰器,那么只有蓝本中的
错误才能触发处理程序。要想注册应用全局的错误处理程序,必须使用 app_errorhandler
装饰器。
视图函数 index() 注册的端点名是 main.index,其 URL 使用 url_for('main.index') 获取。在蓝本中可以省略蓝本名,例如 url_for('.index')。在这种写法中
对蓝本来说, before_request 钩子只能应用到属于蓝本的请求上。若想在
蓝本中使用针对应用全局请求的钩子,必须使用 before_app_request 装饰器
Flask-SQLAlchemy 提供了一个选项,可以记录一次请求中与数据库查询有关的统计数据。
gunicorn flasky:app
flasky:app 告诉 Gunicorn 应用实例的位置,冒号前面的部分是实例所在的包名或模块名,
冒号后面的部分是应用实例的名称。
Gunicorn Web 服务器不能在微软 Windows 中运行
容器中的entrypoint脚本
1 一个视图函数可以绑定多个 URL
2 为了让互联网上的人都可以访问,需要安装程序的服务器有公网ip
3 如果过度使用扩展,在不需要 的地方引人,那么相应也会导致代码不容易维护 ,应该尽量从实际需求出发,只在需要 的时候使用扩展
4 flask、flask扩展、程序自己的环境变量都可以通过flask的app.config作为统一的接口来设置和获取。app.config是字典的子类,所以可以像操作字典那样使用它
5 session也可以像字典一样使用
6 g也可以像字典一样使用
7 配置变量可以存储在python脚本、python类和json格式的文件中
1 after_request 钩子和 after_this_request 钩子必须接收一个响应类对象作为 参数,并且返回同 一 个或更新后的响应对象 。
2 响应报文的首部包含一些关于响应和服务器的信息,这些内容由 F lask 生成 ,视图函数中返回的内容即为响应报文中的主体内容 。
视图菌数可以返回最多由 三个元素组成的元组:响应主体、状态码、首部字段 。其中首部字段可以为字典,或是两元素元组组成的列表 。
3 abort()函数后面不需要return,因为abort后面的代码将不会被执行
设置响应对象的格式:
4 在 Flask 中,如果想要在响应中添加一个 cookie , 最方便的方法是使用 Response 类提供的
set_cookie()方法 。 要使用这个方法,我们需要先使用 make_response()方法手动生成一个响应对象,
response set_cookie (’ name ’, name )
name = request . cookies . get (' name ', ’ Human ’) #从 Cookie 中获取 name值
当我们使用 session 对象添加 cookie 时,数据会使用程序的密钥对其进行签名,加密后的数据存储在一块名为 session 的 cookie 里
使用session 对象存储的 Cookie ,用户可以看到其加密后的值,但无法修改它 。 因为 session 中的内容使用密钥进行签名,一旦数据被修改,签名的值也会变化。 这样在读取时,就会验证失败
这通过 session 对象的 pop 方法删除设置的cookie
默认情况下, session cookie 会在用户关闭浏览器时删除
==========服务器推送技术,除了下表列的方法,还有websocket
为防止攻击,通过jinja的escape进行转义,即把变量标记的内容标记为文本,而不是HTML代码
常见的攻击:sql注入,跨站脚本攻击,跨站伪造请求,频繁请求
url_for生成的是相对url,若要生成绝对url,需要将_external参数设为true
MVC (Model-ViewController ,模型 - 视图-控制器)
查询字符串从问号?开始,以键值对的形式写出,多个键值对之间使用&分隔 。
获取request中的参数值,建议用get requset.args.get(’name’,’Human')
重定向回上一个页面:
redirect(requset.referrer or url_for("hello"))
redirect(requset.args.get("next"),url_for("hello"))
jinja中常用定界符:
语句:{%%}
表达式: {{}}
注释:{##}
Jinjia允许你在模板中使用大部分 Python 对象
{% if %}
{% else %}
{% endif %}
使用ORM的好处:方便切换数据库(支持多种DBMS);ORM帮忙做了些防止sql注入的工作
自己写sql的好处:灵活,快
表名生成规则:
据库和表一旦创建后,之后对模型的改动不会自动作用到实际的表中 。如采要使改动生效,最简单的方式是调用 db. drop all()方法删除数据库和表,然后再调用 db . create_a ll()方法创建
我们在创建模型类实例的时候并没有定义 id 字段的数据,这是因为主键由 SQLAlchemy 管理
一般来说,定义关系需要两步,分别是创建外键和定义关系属性。在更复杂的多对多关系中,我们还需要定义关联表来管理关系 。
动态url:使用“< 变量名>”的形式表示 。 @app . route ( ' /greet /<name> ' )
flask中对应的MVC: M->sqlAlchemy V->jinja2 C->视图函数
请求解析和 响应封装实际上大部分是由 Werkzeug完成的,Flask子类化Werkzeug的请求( request)和响应( Response )对象并添加了和程序相关的特定功能
访问的页面不存在,则会返回404错误
HEAD 、 OPTIONS 方法的请求由Flask 处理 ,而像 DELETE 、 PUT 等方法一般不会在程序中实现,在后面我们构建 WebAPI 时才会用到这些方法
响应报文的首部包含一些关于响应和服务器的信息,这些内容由 F lask 生成
通过定义方法列表,我们可以为同一个 URL 规则定义多个视图函数,分别处理不同 HTTP方法的请求
每个钩子可以注册任意多个处理函数,函数名并不是必须和钩子名称相同,
重定向会导致浏览器地址栏中的url放生变化
不过我们一般并不直接使用 json 模块的 dumps() 、 load() 等方法,因为 Flask 通过包装这些方法提供了更方便的 jsonify()函数
session存储的内容通过签名加密了,但通过工具可以轻易读取(即使不知道秘钥),所以session中不能存储敏感信息
我们可以在视图函数中或在视图函数内调用的函数/方法中使用所有上下文全局变量。
重定向到上一个页面:return redirect(request.referrer or url_for(’ hello ’) ) 手动加入包含当前页面 URL 的查询参数,这个查询参数一般命名为 next。
为了安全,需要对重定向路由做校验
AJAX:让我们可以在不重载页面的情况下和服务器进行数据交换,在接收到响应数据后局部更新页面
使用jQuery发送AJAX请求:jQuery包装了JavaScript,可以更简单的方式编写JavaScript,更方便的操作ajax,同时处理了不通浏览器的AJAX兼容问题
美元符号是 jQuery 的简写,我们通过它来调用 jQuery 提供的多个方法,所以$.ajax()等同于jQuery.jax()
对于处理 AJAX 请求的视图函数来说 ,我们不会返回完整的 HTML 响应,这时一般会返回局部数据
服务器推送技术:websocket(通过TCP实现的全双工模式),兼容性也比SSE更强
安全问题:
1)SQL注入
如 利用url中的参数查数据库
--使用ORM、校验入参类型、参数化查询
2)跨站脚本
如url参数中有js语句 或 提交的留言里有js语句--HTML转义
--HTML转义 、校验入参
3)跨站请求
如B网站的一个图片的src指向了A网站的一个接口,从而对A网站进行了操作
--令牌(伪随机数)放到表单隐藏字段和session中
==============
WSGI 是开发 Python Web 程序的标准,所有的 Python Web 框架都需要按照 WSGI 的规范来
编写程序 。 客户端和服务器端进行沟通遵循了 HTTP 协议,从 HTTP 请求到我们的 Web 程序之间,还有另外一个转换过程一一从 HTTP报文到 WSGI 规定的数据格式。
uWSGI、 Gunicorn 是实现了WSGI协议的web服务器
在Flask中使用 session 非常简单 ,只需要设置好密钥 ,就可以在视图函数中操作session对象
向 session 中存储值 时, 会生成加密的 cookie 加入响 应 。 这 时用户的浏览器接收到响应会将 cookie 存储起来 。 当用户再次发起请求时 , 浏览器会自动在请求报文中加入这个 cookie 值。
Flask 接收到请求会把 session cookie 的值解析到 session 对象里 。 这时我们就可以再次从session中读取 内容 。
Flask 提供的 session 将用户会话存储在客户端 ,和这种存储在客户端的方式相反,另一种 实现用户会话的方式是在服务器端存储用户会话 , 而客户端只存储一个 session ID 。当接收到客户端 的请求时,可以根据 cookie 中的 session ID 来找到对应的用户会话内容。这种方法更为安全和强健,你可以使用扩展 Flask-Session 来实现这种方式的session
当包或包内的模块被导入时 , __init__.py文件将被自动执行 。
从__init__.py中导入变量时不需要注明__init__的路径,只需要从包名称导入
获取UTC时间:
from datetime import datetime
datetime.utcnow()
Flask 允许加载多次配置,重复的配置以最后定义的配置为准
在蓝本中,使用 before_request 、 after_request 、 teardown_request 等装饰器注册的请求处理函数是蓝本独有的
在蓝本中也可以使用 before_app_request 、 after_app_request 、 teardown_app_request 、 before_app first_request 方法,这些方法注册的请求处理函数是全局的
app.register_blueprint(auth bp , url prefix= ’/auth’ )#设置url前缀
app.register_blueprint(auth_bp , subdomain= ’auth’ )# 设置子域名 auth.exarnple.corn/login 的URL才会触发auth蓝本中的login视图
url_for( ’ auth.login ’ ) #在全局生成蓝本的url,需要带上蓝本名字。若在蓝本内调用,可以使用url_for('.login')
若你在注册蓝本时为蓝本定义了 URL 前缀,即设置了 url_prefix ,那么最终静态文件路径会自动设为“/蓝本前缀/static ”,这时可以省略 static_url_path 的定义 。
通过给蓝图的before_request加login_required装饰,从而实现对整个蓝图的所有函数加了登录保护
可以在flash消息中添加视图连接,Flash提供的Markup类可以将文本标记为安全文本,从而避免渲染时对Jinjia2进行转义
装饰器执行顺序:
URL样例:http ://api.example.com
版本:https://developer.github.com/v3/, 有的将版本放在head里
Flask 在 MethodView 类中提供了 decorators属性,使用它可以为整个资掘类的所有视图方法附加装饰器
app.config["call_index"] = 0 import threading lock = threading.Lock() def tst(): with lock: current_app.config['call_index'] = current_app.config['call_index'] + 1 index = current_app.config.get('call_index') t = str(int(time.time()) imgfile = os.path.join(BASE_DIR, t + str(index) + ".jpg")