流程控制-选择结构
所有的控制语句都是放在 {% ... %} 中,并且有一个语句 {% endxxx %} 来进 行结束!
if:if语句和python中的类似,可以使用 >,=,==,!= 来进行判 断,也可以通过 and,or,not,() 来进行逻辑合并操作
{% if age >= 18 %} <p>{{ age }}岁,成年人,可以通宵打游戏</p> {% else %} <p>{{ age }}岁,未成年人,可以通宵学习</p> {% endif %}
注意:
if 条件判断语句必须放在 {% if statement %} 中间,并且还必须有结束 的标签 {% endif %} 。
流程控制-循环结构
for...in... for循环可以遍历任何一个序列包括列表、字典、元组。并且 可以进行反向遍历,以下将用几个例子进行解释:
列表
<ul> {% for user in users%} <li>{{ user}}</li> {% endfor %} </ul>
遍历字典
<tr> {% for key in person.keys() %} <td>{{ key}}</td> {% endfor %} </tr> <tr> {% for val in person.values() %} <td>{{ val}}</td> {% endfor %} </tr> <tr> {% for item in person.items() %} <td>{{ item}}</td> {% endfor %} </tr> <tr> {% for key,value in person.items() %} <td>{{ value}}</td> {% endfor %} </tr>
如果序列中没有值的时候,进入else 反向遍历用过滤器 reverse:
<ul> {% for user in users|reverse %} <li>{{ user}}</li> {% else %} <li>没有任何用户</li> {% endfor %} </ul>
并且Jinja中的for循环还包含以下变量,可以用来获取当前的遍历状 态:
总结
在 jinja2 中的 for 循环,跟 python 中的 for 循环基本上是一模一样的 也是 for...in... 的形式。并且也可以遍历所有的序列以及迭代器 唯一不同的是, jinja2 中的 for 循环没有 break 和 continue 语句
导入模板include
1. 这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位 置。
2. include 标签,如果想要使用父模版中的变量,直接用就可以了,不 需要使用 with context 。
3. include 的路径,也是跟 import 一样,直接从 templates 根目录下去找, 不要以相对路径去找。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>SXT</title> </head> <body> <!--通过include 引入头部log信息--> {% include "common/head.html" %} <div> 这是首页内容 {{ major }} </div> <hr> <!--通过include 引入底部版权信息--> {% include "common/footer.html" %} </body> </html>
set与with标签
set的使用
在模版中,可以使用 set 语句来定义变量
<!--set语句来定义变量,之后,那么在后面的代码中,都可以使用这个变量--> {% set uname='sxt'%} <p>用户名:{{ uname }}</p>
一旦定义了这个变量,那么在后面的代码中,都可以使用这个变 量,就类似于Python的变量定义是一样的
with语句
with 语句定义的变量,只能在 with 语句块中使用,超过了这个代码 块,就不能再使用了
<!--with语句来定义变量,只有在指定区域 才能使用这个变量--> {% with classroom='python202'%} <p>班级:{{ classroom }}</p> {% endwith %}
注意
关于定义的变量, with 语句也不一定要跟一个变量, 可以定义一个空的 with 语句, 需要在指定的区域才能使用的情况,可以set与with组合使用。
{% with %} {% set pname='李思思' %} <p>娱乐县县长:{{ pname }}</p> {% endwith %}
静态文件
静态文件:css文件 js文件 图片文件等文件
加载静态文件使用的是 url_for 函数。然后第一个参数需要为 static ,第 二个参数需要为一个关键字参数 filename='路径' 。
语法
{{ url_for("static",filename='xxx') }}
注意 路径查找,要以当前项目的 static 目录作为根目录
模板继承
为什么需要模版继承
模版继承可以把一些公用的代码单独抽取出来放到一个父模板中 以后子模板直接继承就可以使用了。 这样可以重复的利用代码,并且以后修改起来也比较方便
模版继承语法
使用 extends 语句,来指明继承的父模板。父模板的路径,也是相对 于 templates 文件夹下的绝对路径
{% extends "base.html" %}
block语法
一般在父模版中,定义一些公共的代码。子模板可能要根据具体的 需求实现不同的代码。这时候父模版就应该有能力提供一个接口,让子模板来实现。从而 实现具体业务需求的功能。
父模板
{% block block的名字 %} {% endblock %}
子模板
1. {% block block的名字 %} 2. 子模板中的代码 3. {% endblock %}
调用父模版代码block中的代码
默认情况下,子模板如果实现了父模版定义的block。那么子模板 block中的代码就会覆盖掉父模板中的代码。 如果想要在子模板中仍然保持父模板中的代码,那么可以使用 {{ super( ) }} 来实现
父模板
{% block block_body %} <p style="background-color: blue">我是父模版block_body处的内容</p> {% endblock %}
子模板
1. {% block block_body%} 2. {{ super() }} 3. <p style="background-color: green">我是子模版block_body处的内容</p> 4. {% endblock %}
调用另外一个block中的代码
如果想要在另外一个模版中使用其他模版中的代码。那么可以通过 {{ self.其他block名字() }} 就可以了
{% block title %} sxt首页 {% endblock %} {% block block_body%} {{ self.title() }} <p style="background-color: green">我是子模版block_body处的内容</p> {% endblock %}
注意
1. 子模板中的代码,第一行,应该是 extends
2. 子模板中,如果要实现自己的代码,应该放到block中。如果 放到其他地方,那么就不会被渲染
Flask视图
add_url_rule与app.route
add_url_rule
add_url_rule(rule,endpoint=None,view_func=None)
这个方法用来添加url与视图函数的映射。 如果没有填写 endpoint ,那么默认会使用 view_func 的名字作为 endpoint 。 以后在使用 url_for 的时候,就要看在映射的时候有没有传递 endpoint 参 数,如果传递了,那么就应该使用 endpoint 指定的字符串, 如果没有传递,那么就应该使用 view_func 的名字。
def my_list(): return "我是列表页" app.add_url_rule('/list/',endpoint='sxt',view_func=my_list)
app.route原理剖析
这个装饰器底层,其实也是使用 add_url_rule 来实现url与视图函数映射 的。
from flask import Flask,url_for app = Flask(__name__) @app.route('/',endpoint='index') def index(): print(url_for('show')) print(url_for('index')) return "Hello" def show_me(): return "这个介绍信息!!" # endpoint 没有设置,url_for中就写函数的名字,如果设置了,就写endpoint的值 app.add_url_rule('/show_me',view_func=show_me,endpoint='show') # @app.route 底层就是使用的 add_url_rule if __name__ =='__main__': app.run(debug=True)
类视图
之前我们接触的视图都是函数,所以一般简称函数视图。 其实视图也可以基于类来实现,类视图的好处是支持继承, 但是类视图不能跟函数视图一样,写完类视图还需要通过 app.add_url_rule(url_rule,view_func) 来进行注册
标准类视图使用步骤
1. 标准类视图,必须继承自 flask.views.View
2. 必须实现 dispatch_request 方法,以后请求过来后,都会执行这个方 法。 这个方法的返回值就相当于是之前的视图函数一样。也必须返回 Response 或者子类的对象,或者是字符串,或者是元组。
3. 必须通过 app.add_url_rule(rule,endpoint,view_func) 来做url与视图的映射。 view_func 这个参数,需要使用类视图下的 as_view 类方法类转换: ListView.as_view('list') 。
4. 如果指定了 endpoint ,那么在使用 url_for 反转的时候就必须使用 endpoint 指定的那个值。如果没有指定 endpoint ,那么就可以使用 as_view(视图名字) 中指定的视图名字来作为反转。
from flask import Flask,url_for from flask.views import View app= Flask(__name__) @app.route('/') def index(): # print(url_for('mylist')) print(url_for('my')) return 'Hello' class ListView(View): def dispatch_request(self): return '返回了一个List的内容!!' # app.add_url_rule('/list',view_func=ListView.as_view('mylist')) app.add_url_rule('/list',endpoint='my',view_func=ListView.as_view('mylist')) # 用于测试 with app.test_request_context(): print(url_for('my')) if __name__ =='__main__': app.run(debug=True)
类视图的好处
1.可以继承,把一些共性的东西抽取出来放到父视图中,子视图直 接拿来用就可以了。
2.但是也不是说所有的视图都要使用类视图,这个要根据情况而 定。视图函数用得最多。
from flask import Flask,jsonify from flask.views import View app = Flask(__name__) # 需求:返回的结果都必须是json数据 class BaseView(View): def get_data(self): raise NotImplementedError def dispatch_request(self): return jsonify(self.get_data()) class JsonView(BaseView): def get_data(self): return {'uname':'吕布','age':20} class Json2View(BaseView): def get_data(self): return [ {'name':'zs','lua':'Python'}, {'name':'lisi','lua':'Python'}, ] app.add_url_rule('/base',view_func=BaseView.as_view('base')) app.add_url_rule('/json',view_func=JsonView.as_view('json')) app.add_url_rule('/json2',view_func=Json2View.as_view('json2')) if __name__ =='__main__': app.run(debug=True)
基于调度方法的类视图
1. 基于方法的类视图,是根据请求的 method 来执行不同的方法的。 如果用户是发送的 get 请求,那么将会执行这个类的 get 方法。 如果用户发送的是 post 请求,那么将会执行这个类的 post 方法。其他 的method类似,比如 delete 、 put
2. 这种方式,可以让代码更加简洁。所有和 get 请求相关的代码都放 在 get 方法中,所有和 post 请求相关的代码都放在 post 方法中。就不 需要跟之前的函数一样,通过 request.method == 'GET'
class LoginView(views.MethodView): def get(self,error=None): return render_template('login.html',error=error) def post(self): #模拟实现 #拿到前端页面传过来的 账号 和密码 去数据库做查询操作 查询到 (跳转主页面) ,反之跳转到login.html页面并给出错误提示信息 uname = request.form['uname'] pwd = request.form['pwd'] if uname=="sxt" and pwd =="123": return render_template('index.html') else: return self.get(error="用户名或者密码错误") # 注册类视图 app.add_url_rule('/login/',view_func=LoginVi ew.as_view('my_login'))
HTML
<form action="/login/" method="post"> <table> <tr> <td>账号:</td> <td><input type="text" name="uname"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="pwd"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即登录"></td> </tr> <tr> <td colspan="2"> {# <font color="red">{{ error }}</font>#} {# 优化写法 :判断 #} {% if error %} <font color="red">{{ error }}</font> {% endif %} </td> </tr> </table> </form>
装饰器
简言之,python装饰器就是用于拓展原来函数功能的一种函数,这 个函数的特殊之处在于它的返回值也是一个函数, 使用python装饰器的好处就是在不用更改原函数的代码前提下给函 数增加新的功能。
1. 在视图函数中使用自定义装饰器,那么自己定义的装饰器必须放 在 app.route 下面。 否则这个装饰器就起不到任何作用。
案例1
需求:查看设置个人信息时,只有检测到用户已经登录了才能查 看,若没有登录,则无法查看并给出提示信息
定义装饰器
def login_required(func): @wraps(func) def wrapper(*arg,**kwargs): uname = request.args.get('uname') pwd = request.args.get('pwd') if uname == 'zs' and pwd == '123': logging.info(f'{uname}:登录成功') return func(*arg,**kwargs) else: logging.info(f'{uname}:尝试登录,但没成功') return '请先登录' return wrapper
使用装饰器
@app.route('/settings/') @login_requierd def settings(): return '这是设置界面'
2. 在类视图中使用装饰器,需要重写类视图的一个类属性 decorators , 这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰 器。
案例2
需求: 查看设置个人信息时,只有检测到用户已经登录了才能查看, 若没有登录,则无法查看并给出提示信息
使用装饰器
class ProfileView(views.View): decorators = [login_requierd] def dispatch_request(self): return '这是个人中心界面' app.add_url_rule('/profile/',view_func=ProfileView.as_view('profile'))
蓝图介绍
在Flask中,使用蓝图Blueprint来分模块组织管理。 蓝图实际可以理解为是存储一组视图方法的容器对象,其具有如下 特点:
一个应用可以具有多个Blueprint
可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user” 、 “/goods”
Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应 用的视图和函数的
在一个应用初始化时,就应该要注册需要使用的Blueprint
注意
Blueprint并不是一个完整的应用,它不能独立于应用运行,而 必须要注册到某一个应用中
使用方式
使用蓝图可以分为三个步骤
- 1 创建一个蓝图对象
user_bp=Blueprint('user',__name__)
- 在这个蓝图对象上,
1. @user_bp.route('/') 2. def user_profile(): 3. return 'user_profile'
- 在应用对象上注册这个蓝图对象
app.register_blueprint(user_bp)
指定蓝图的url前缀
在应用中注册蓝图时使用 url_prefix 参数指定
app.register_blueprint(user_bp,url_prefix='/user') app.register_blueprint(goods_bp,url_prefix='/goods')
蓝图的目录结构
为了让项目代码更加清晰,可以通过将代码分在不同的文件里进行 管理
根据功能模块
对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到 Python包的 __init__.py 文件中
--------- project # 工程目录 |------ main.py # 启动文件 |------ user #用户蓝图 | |--- __init__.py # 此处创建蓝图对象 | |--- view.py | |--- ... |------ goods # 商品蓝图 | |--- __init__.py | |--- ... |...
根据技术模块
--------- project # 工程目录 |------ main.py # 启动文件 |------ view #用户蓝图 | |--- user.py # 此处创建蓝图对象 | |--- item.py | |--- view.py | |--- ... |...
蓝图中模版文件
寻找规则
- 如果项目中的templates文件夹中有相应的模版文件,就直接使 用了。
- 如果项目中的templates文件夹中没有相应的模版文件,那么就 到在定义蓝图的时候指定的路径中寻找。
- 并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录
因为这个蓝图文件是在user/view.py,那么就会到blueprints这个 文件夹下的user_page文件夹中寻找模版文件。
小总结: 常规:蓝图文件在查找模版文件时,会以templates为根目录进行 查找
注意
1 个性化coder喜欢在【创建蓝图对象的时候】 指定 模版文 件的查找路径,如下 news_bp =Blueprint('news',__name__,url_prefix='/news',template_folder='news_page')
2 只有确定templates目录下没有对应的 html文件名的时候, 才会去蓝图文件指定的目录下查找,指定才会生效
3 若templates目录下,有一个与蓝图文件指定的目录下同名 的一个 html文件时,优先走templates目录下的东西
蓝图中静态文件
蓝图内部静态文件 蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时 指定 static_folder 参数。 下面的示例将蓝图所在目录下的 static_admin 目录设置为静态目录:
user=Blueprint("user",__name__,static_folder='user_static') app.register_blueprint(admin,url_prefix='/user')
也可通过 static_url_path 改变访问路径
user =Blueprint('user',__name__,template_folder='user_page',static_folder='user_static',static_u rl_path='/static') app.register_blueprint(user,url_prefix='/user')
总结 【掌握】查找方式1:查找静态文件时,正常情况下,会以 static为根目录进行查找 【了解】查找方式2:查找静态文件时,非正常情况下,需要用 url_for('蓝图的名字.static'),然后会去蓝图对象在创建时指定的 静态文件夹目录下 去查找静态文件
蓝图url_for函数
如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,就应该 在使用url_for的时候指定这个蓝图名字。 app类中、模版中、同一个蓝图类中都是如此。否则就找不到这个 endpoint
html文件中
<a href="{{ url_for('user.user_list')}}">新闻列表 OK写法</a> {# <a href="{{ url_for('user_list')}}">新闻列表 no Ok写法</a>#}
python文件中
from flask import Blueprint,render_template,url_for user_bp=Blueprint('news',__name__,url_prefix='/user',template_folder='user_page',static_folder='user_static') @user_bp.route('/list/') def user_list(): #如下写法:才找得到 url_for('蓝图名称.方法名') print(url_for('user.user_list')) #/user/list/ print(url_for('user.user_detail')) #/user/detail/ return render_template('user_list.html') @user_bp.route('/detail/') def user_detail(): return '用户详情页面'
子域名实现
蓝图实现子域名:
1. 使用蓝图技术。
2. 在创建蓝图对象的时候,需要传递一个 subdomain 参数,来指定这 个子域名的前缀。
cms_bp=Blueprint('cms',__name__,subdomain='cms')
3. 需要在主app文件中,需要配置app.config的SERVER_NAME参 数。例如:
app.config['SERVER_NAME']='baidu.com:5000'
4. 在windows: C:\Windows\System32\drivers\etc 下,找到hosts文件,然后添 加域名与本机的映射。Linux: /etc/hosts 域名和子域名都需要做映射
注意 ip地址不能有子域名 localhost也不能有子域名
Flask高级
Flask设置Cookie
设置
设置cookie是在Response的对象上设置。 flask.Response 对象有一个 set_cookie 方法,可以通过这个方法来设置 cookie 信息。
key,value形式设置信息
from flask import Flask, make_response app = Flask(__name__) @app.route('/cookie') def set_cookie(): resp = make_response('set cookie ok') resp.set_cookie('uname', 'itbaizhan') return resp
查看Cookie
在Chrome浏览器中查看cookie的方式:
方式1:借助于 开发调式工具进行查看
方式2:在Chrome的设置界面->高级设置->内容设置->所有 cookie->找到当前域名下的cookie。
from flask import request @app.route('/get_cookie') def get_cookie(): resp = request.cookies.get('uname') return resp
删除cookie
方式1:通过 Response对象.delete_cookie ,指定cookie的key,就可以删 除cookie了。
from flask import request @app.route('/delete_cookie') def delete_cookie(): response = make_response('helloworld') response.delete_cookie('uname') return response
方式2:在客户端浏览器人为的删除(清除浏览器浏览历史记录 后,很多网站之前免密登录的都不好使了)
Cookie的有效期
默认的过期时间:如果没有显示的指定过期时间,那么这个cookie 将会在浏览器关闭后过期。 max_age:以秒为单位,距离现在多少秒后cookie会过期。
expires:为datetime类型。这个时间需要设置为格林尼治时间, 相对北京时间来说 会自动+8小时 如果max_age和expires都设置了,那么这时候以max_age为标 准。
注意
max_age在IE8以下的浏览器是不支持的。 expires虽然在新版的HTTP协议中是被废弃了,但是到目前为 止,所有的浏览器都还是能够支持,所以如果想要兼容IE8以下 的浏览器,那么应该使用expires,否则可以使用max_age。
from flask import Flask,Response app = Flask(__name__) @app.route('/') def index(): return 'Hello!!' @app.route('/create_cookie/defualt/') def create_cookie1(): resp = Response('通过默认值,设置cookie有效期') # 如果没有设置有效期,默认会在浏览器关闭的时候,让cookie过期 resp.set_cookie('uname','zs') return resp @app.route('/create_cookie/max_age/') def create_cookie2(): resp = Response('通过max_age,设置cookie有效期') # max_age以秒为单位设置cookie的有效期 age = 60*60*2 resp.set_cookie('uname','zs',max_age=age) return resp from datetime import datetime @app.route('/create_cookie/expires/') def create_cookie3(): resp = Response('通过expires,设置cookie有效期') # expires 以指定时间为cookie的有效期 # 16+8 == 24 tmp_time = datetime(2021, 11,11,hour=18,minute=0,second=0) resp.set_cookie('uname','python',expires=tmp_time) return resp from datetime import timedelta @app.route('/create_cookie/expires2/') def create_cookie4(): resp = Response('通过expires,设置cookie有效期') # expires 以指定时间为cookie的有效期 tmp_time = datetime.now() +timedelta(days=2) resp.set_cookie('uname','python_sql',expires=tmp_time) return resp @app.route('/create_cookie/exp_max/') def create_cookie5(): resp = Response('通过expires与max_age,设置cookie有效期') # expires 与max_age同时设置了,会以max_age为准 tmp_time = datetime.now() +timedelta(days=2) resp.set_cookie('uname','python_sql',expires=tmp_time,max_age = 60*60*2) return resp if __name__ == '__main__': app.run(debug=True)