虚拟环境
虚拟环境顾名思义就是虚拟的,在这里面装任何软件都不会影响到其他的程序,类似与一个抽屉。
使用虚拟环境的好处是:让电脑中安装很多种解释器,并且互不影响
virtualenv的使用
安装virtualenv
通过pip或者pip3来安装:pip install virtualenv
可以用pip list 来查看都安装了那些包
创建虚拟环境
virtualenv [虚拟环境的名字]
就是在当前目录下创建了一个文件夹,文件夹中有好多文件
进入虚拟环境
windows系统:需要进入进入到虚拟环境的 Scripts 文件夹中,然后执行 activate。
Liunx系统:进入虚拟环境: source /path/to/virtualenv/bin/activate。
退出虚拟环境
deactivate :退出虚拟环境命令
virtualenvwrapper的使用
安装virtualenvwrapper
Liunx:pip install virtualenvwrapper
windows: pip install virtualenvwrapper-win
创建虚拟环境
mkvirtualenv my_env
并且默认文件位置是在C://Administor/Envs中
切换某个虚拟环境
workon my_env
退出当前虚拟环境
deactivate
删除某个虚拟环境
rmvirtualenv my_env
列出所有虚拟环境
lsvirtualenv
进入到虚拟环境所在的目录
cdvirtualenv
修改mkvirtualenv 的默认路径
在 我的电脑->右键->属性->高级系统设置->环境变量->系统变量 中添加一个参数 WORKON_HOME ,将这个参数的值设置为你需要的路径,并且重新打开dos窗口才生效。
创建虚拟环境的时候指定 Python 版本
mkvirtualenv --python==C:\Python36\python.exe hy_env
Flask视图基础和URL
认识URL
URL : Uniform Resource Locator 的简写,统一资源定位符。一个 URL 由以下几部分组成:scheme://host:port/path/?query-string=xxx#anchorscheme:代表的是访问的协议,一般为 http 或者 https 以及 ftp 等。
host:主机名,域名,比如 www.baidu.com。
port:端口号。当你访问一个网站的时候,浏览器默认使用80端口。
path:查找路径。比如: www.jianshu.com/trending/now,后面的 trending/now就是 path。
query-string:查询字符串,也叫请求参数,比如: www.baidu.com/s?wd=python,后面的 wd=python就是查询字符串。
anchor:锚点,后台一般不用管,前端用来做页面定位的。
注意: URL 中的所有字符都是 ASCII 字符集,如果出现非 ASCII 字符,比如中文,浏览器会进行编码再进行传输。
web服务器和应用服务器以及web应用框架
web服务器:负责处理http请求,响应静态文件,常见的有 Apache,Nginx以及微软的IIS
应用服务器:负责处理逻辑的服务器。比如 php 、 python 的代码,是不能直接通过 nginx 这种web服务器来处 理的,只能通过应用服务器来处理,常见的应用服务器有 uwsgi 、tomcat 等。
web应用框架:一般使用某种语言,封装了常用的 web 功能的框架就是web应用框架, flask、Django以及 Java中的 SSM(Spring+SpringMVC+Mybatis) 框架都是web应用框架。
三者的应用顺序,以及职责所在
第一个Flask框架程序(代码认识)
# 从flask中导入Flask
# Flask这个类是项目的核心,以后很多操作都是基于这个类对象
# 注册url、注册蓝图等等都是基于这个类对象
from flask import Flask
app = Flask(__name__)
# 是一个装饰器,将url中/映射到hello_world这个视图函数上,
# 当以后访问这个网站的/目录是,就会执行hello_world这个函数,然后将函数的返回值返回给浏览器
# 例如:www.baidu.com/ --> hello_world执行
@app.route('/')
def hello_world():
print('hhah')
return '你好呀~'
@app.route('/list/')
def my_list():
return '我就是我哦~'
if __name__ == '__main__':
# 默认为5000端口
# app.run()
app.run(port=8000)
开启Flask框架的DeBug模式
每个版本的PyCharm开启的方式不同,下面所记录是在PyCharm2017中的开启方式。
开启方式直接在运行的地方,添加app.run(debug=True)
在代码中间,添加app.debug= True
同样是在代码中间,app.config.update(DEBUG=True)
通过配置文件开启创建config.py文件,先引入(import config),然后执行app.config.form_object(config)
创建config文件,不局限py文件,普通文本文件也可以,执行:app.config.form_pyfile(‘config.txt’)
注意文件路径要写全名,后缀名也要写,不然会导致整个项目挂掉,解决方式:silent =True
URL与函数映射
传递参数的两种方式
/路径/参数,(就是将参数嵌入到路径中)
代码:
@app.route('/article//')
def article_detail(id):
return '您请求的文章是:%s'% id
其中的,尖括号是固定用法,语法为,variable默认的数据类型是字符串(string),如果想要指定数据类型,则要写成,其中的converter的类型有如下的六种:string:字符串,如果没有指定具体的数据类型,那么默认为字符串
int
float
path:path数据类型和字符串类型类似,区别在与一点,就是对于‘/’右斜杠的处理。
uuid:uuid是一个全宇宙都唯一的字符串,一般可以用来作为表的主键。
any:数据类型可以在一个url中指定多个路径。例如: @app.route('/any(user,blog):url_path/') def detail(url_path,id): if url_path=='blog': return "博客详情:%s" %id else : return "用户详情:%s" %id
/路径?参数名1=参数值1&参数名2=参数值2...
代码:
from flask import request,render_template
# 第二种:/路径?参数名1=参数值1&参数名2=参数值2...,
# @app.route('/list7') # 这种写法只支持get请求方式,不支持post请求方式
@app.route('/list7',methods=['GET','POST'])
# 这种写法及支持get有支持post
def list7():
if request.method=='GET':
uname = request.args.get('uname')
pwd = request.args.get('pwd')
return render_template('login.html')
elif request.method =='POST':
uname = request.form.get('uname')
pwd = request.form['pwd']
return 'POST方式接收到的参数为;%s,%s'%(uname,pwd)
要注意,两种请求方式的区别!!!
url_for的使用详解
之前都是用一个url调用函数,这次反过来了,给定函数生成url,这个功能的实现依托于url_for。
url_for接收两个以上的参数,并且接收到的函数名作为第一个参数(这个是规定),然后接收对应的URL规则命名参数,如果还有其他的参数,会自动添加到URL后面作为查询参数。
使用代码:
@app.route('/')
def hello_world():
# return 'Hello World!'
# 希望返回页面一个指定的url,如'/list/'
# return '/list/'
# 使用url_for函数构建url
# 这么做的好处:如果将来修改了url 但是没有修改url对应的函数名,就不用了到处替换url了(路径变化概率要大于函数名变化)
# return url_for('list1') #******
# 构建url 如/list2/3/
# return url_for('list2',page=3) #******
# 构建url 如:/list2/4/?num=8&pwd=123 了解
# return url_for('list2',page=4,num=8,pwd=123)
# 这么做的好处:url_for() 函数会转义一些特殊字符和 unicode 字符串,这些事情 url_for 会自动的帮我们搞定。
return url_for('list1',next='/')
参数底层的原理
Flask项目中,底层是如何实现参数类型格式判断的呢?
根据werkzeug.routing模块中的BaseConverter类中的每个分类确定的。
每个数据类型都是调用werkzeug.routing模块中的对应类来做格式判断的!
自定义URL转换器
步骤:实现一个类,继承自BaseConverter。
在自定义的类中,重写regex,也就是这个变量的正则表达式。
将自定义的类,映射到app.url_map.converters上。理解为加入字典DEFAULT_CONVERTERS中 书写格式:app.url_map.converters['tel']=TelephoneConveter
代码:
# 需求1:希望路径中能匹配一个电话号码类型的参数
class TelephoneConverter(BaseConverter):
regex = r"1[345789]\d{9}"
app.url_map.converters['tel']=TelephoneConverter
# 使用自定义转换器实现需求
@app.route('/telephone/')
def my_telephone(pnum):
return '您请求过来的电话号码值为:%s'%pnum
to_python和to_url方法
to_python:可以把url拆分,并且返回txt格式的文件。
to_url:在调用url_for函数的时候,生成一个合适的url形式
代码:
# 需求2:查询多个模块的数据
# 自定义转换器实现
class LiConverter(BaseConverter):
def to_python(self, value):
# value是自己传递过来的参数
# 可以对value进行加工后在返回
lm = value.split('+')
print(lm)
print(lm[0])
print(lm[1])
return lm
def to_url(self, value):
# 目的是要把['hots','enter']---->hots+enter
return '+'.join(value)
app.url_map.converters['li']=LiConverter
@app.route('/news_list2/')
def news_list2(moudles2):
print(moudles2)
# 此时参数已经进行了拆分 slect *from news where nmoudles='hots' or nmoudles='enter'
return "您要查询的模块是:%s" % moudles2
@app.route('/hello/')
def hello_wrold():
# 构建url
args = url_for('news_list2',moudles2=['hots','enter'])
return '构建出url并返回:%s'%args
页面跳转和重定向
重定向分为暂时性重定向和永久性重定向,也就是在浏览器上会自动跳转到另一个页面。
永久性重定向: http 的状态码是 301 ,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,输入 www.jingdong.com 的时候,会被重定向到 www.jd.com。
暂时性重定向:http 的状态码是 302 ,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
Flask中的重定向
重定向是通过 redirect(location,code=302) 这个函数来实现的。
location 表示需要重定向到的 URL,应该配合之前讲的 url_for()函数来使用。
code 表示采用哪个重定向,默认是 302( 暂时性重定向 ) ,可以修改成 301来实现永久性重定向。
代码:
from flask import Flask,request,url_for,redirect
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
# 登录界面
@app.route('/login/')
def login():
return '请登录'
# 页面跳转,暂时性重定向302 永久性重定向301
@app.route('/profile/')
def profile():
if request.args.get('name'):
return '用户中心页面'
else:
return redirect(url_for('login'),code=302)
app.debug=True
if __name__ == '__main__':
app.run()
Response返回值
Response是返回值的意思,类似于return的功能, 相当于一个包装,把返回的内容包装起来用于其他的操作。
自定义Response对象的步骤继承自Response类。
实现方法force_type(cls,rv,environ=None)。
指定app.response_class为你自定义的Response对象。
代码:
# 需求:希望将一个字典类型的数据,变成json对象返回给客户端
# 问题:视图函数不支持返回字典类型的数据
# 解决:自定义Response的子类对象来解决
# 步骤:
# 1.继承自`Response`类。
# 2.实现方法`force_type(cls, rv, environ=None)`。
# 3.指定`app.response_class`为你自定义的`Response`对象。
class JSONResponse(Response):
@classmethod
# 因为是类方法
def force_type(cls, response, environ=None):
'''这个方法只有视图函数返回 非字符串 非Response对象 非元组时才会调用response:视图函数的返回值'''
if isinstance(response,dict):
# 当返回类型为字典的时候,在加工操作
# 希望吧一个字典对象转化为json对象
resp = jsonify(response)
return super(JSONResponse, cls).force_type(resp)
app.response_class = JSONResponse
@app.route('/myprofile/')
def profile():
return {'uname':'wo','gender':'nan','school':'sxt'}
思维导图
查找路径
引入html文件要借助render_template方法。
@app.route('/')def hello_world():# return 'Hello World!'# 去数据库拿取数据# 模拟数据库的一个用户数据uname = 'momo'return render_template('index.html',content=uname)# 查询所有的新闻信息@app.route('/news/')def news_list():return render_template('news/news_list.html')
模板传参以及技巧
关键字传参(针对参数少的情况)
@app.route('/')
def hello_world():
# 关键词传参,只适用于键值对形式,且参数较少的情况
return render_template('index.html',uname='momo')
传参技巧(针对参数很多的情况)
使用传参技巧
@app.route('/list/')
def hello():
# 如果参数太多了,可以放到一个字典中,然后在传这个字典参数的时候,使用两个星号,将字典打散成关键字参数(也叫命名参数)。
context = {
'uname': 'momo',
'age': 18,
'country': 'china',
'childrens': {
'name': 'mjz',
'height': '62cm'
}
}
# 使用的传参技巧
# return render_template('index.html',**context)
不使用传参技巧
# 不是用传参技巧
return render_template('index.html',c=context)
过滤器
相当于一个函数,把当前变量传入到过滤器中,然后过滤器根据自己功能那个,返回对应的值,然后再将结果渲染到页面上
基本用法
‘{{ variables | 过滤器名字 }}’
使用管道符‘|’进行组合。
介绍+一些常用过滤器
过滤器是通过管道符号(|)进行使用的,例如:{{ name|length }},将返回name的长度abs(value):返回一个数值的绝对值。 例如:-1|abs。
default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。name|default('xiaotuo')——如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。
escape(value)或e:转义字符,会将<、>等符号转义成HTML中的符号。例如:content|escape或content|e。
first(value):返回一个序列的第一个元素。names|first。
format(value,*arags,**kwargs):格式化字符串。例如以下代码: {{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!
last(value):返回一个序列的最后一个元素。示例:names|last。
length(value):返回一个序列或者字典的长度。示例:names|length。
join(value,d=u''):将一个序列用d这个参数的值拼接成字符串。
safe(value):如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe。
int(value):将值转换为int类型。
float(value):将值转换为float类型。
lower(value):将字符串转换为小写。
upper(value):将字符串转换为小写。
replace(value,old,new): 替换将old替换为new的字符串。
truncate(value,length=255,killwords=False):截取length长度的字符串。
striptags(value):删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格。
trim:截取字符串前面和后面的空白字符。
string(value):将变量转换成字符串。
wordcount(s):计算一个长字符串中单词的个数。
default过滤器使用
以个性签名为举例
用法
@app.route('/')
def hello_world():
context ={
'postion':-1,
'signature':'偶买噶'
}
return render_template('index.html',**context)
注意:当value为一些指定的特定值之后,需要给default添加boolean = True.
拓展知识(可以用or代替default过滤器)
个性签名[使用过滤器]:{{ signature|default('此人很懒,暂无签名',boolean = True) }}
个性签名[不是使用过滤器]:{{ signature or ('此人很懒,暂无签名') }}
常用过滤器讲解01
escape过滤器(转义)
JinJa2模板默认全局开启了自动转义功能 意思是:会把'<'转换位html知识里面的实体字符'<'
safe(关闭转义)
如果开启了全局转义,那么safe过滤器会将变量关掉转义。
小总结
autoescapejinja标签,可以对他里面的代码块关闭或开启自动转义。
{% autoescape off/on %}
...代码块
{% endautoescape %}
常用过滤器讲解02
frist+last+length
第一个人:{{ persons|first }}
最后一个人:{{ persons|last }}
总人数:{{ persons|length }}
format
{{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!
helloo对应第一个%s
Foo!对象第二个%s
int
{% if gender|int ==1 %}
性别:男
{% else %}
性别:女
{% endif %}
自定义过滤器
步骤
#4、自定义过滤器
# 需要是用一个装饰器:@app.template_filter('过滤器名称')
@app.template_filter('cut')
def cut_world(value):
value = value.replace('十大酷刑','****')
return value
注意
自定义过滤器需要一个装饰器:
@app.template_filter('过滤器名称')
由于工具不同,所以要把模板设置为自动加载模式
app.config['TEMPLATES_AUTO_RELOAD']=True
自定义时间过滤器
逻辑顺序差值小于60s 返回刚刚
差值大于60s小于360s 返回XX分钟之前
差值大于360s小于24小时 返回XX小时前
差值大于24小时 返回XX天前
代码实现
@app.template_filter('time_to_time')
def time_to_time(time):
if isinstance(time,datetime):
now = datetime.now()
timestap = (now-time).total_seconds()
if timestap<60:
return '刚刚'
elif timestap>60 and timestap<60*60:
minutes = timestap//60
return '%s分钟之前'%minutes
elif timestap>60*60 and timestap<60*60*24:
hours = timestap//(60*60)
return '%s小时之前'%hours
elif timestap>60*60*24:
days = timestap//(60*60*24)
return '%s天之前'%days
else:
return time .strftime('%Y/%m/%d %H:%M')
else:
return time
控制语句
所有的控制语句都是放在{%.......%}中的
if语句
格式
{% if statement(条件语句)%}
...(里面加elif或者else都可以)
{% endif %}
应用案例1(简单案例)
简单判断
学习Jinja2模板语言的if语句
{% if name=='momo' %}
姓名:momo
{% endif %}
{% if age>=18 %}
年龄大于{{ age }}可以进入网吧
{% else %}
未成年(年龄小于{{ age }})不可以进网吧
{% endif %}
{% if score>=90 %}
{{ name }}的成绩为:A
{% elif score>=80 %}
{{ name }}的成绩为:B
{% elif score>=60 %}
{{ name }}的成绩为:C
{% else %}
{{ name }}不及格
{% endif %}
应用案例2(标签中的案例)
户籍:
{% if addr=='bj' %}
-请选择-
-北京市-
-上海市-
-重庆市-
{% elif addr== 'cq' %}
-请选择-
-北京市-
-上海市-
-重庆市-
{% else %}
-请选择-
-北京市-
-上海市-
-重庆市-
{% endif %}
性别:
{% if gender|int==1 %}
男
女
{% else %}
男
女
{% endif %}
for语句
注意在Jinja2的for语句中是没有break和contiune语句的
格式:
遍历列表:
{% for user in users %}
{{ user }}{% else %}
没有任何用户{% endfor %}
遍历字典:
{% for key in person.keys()[或者values(),item()] %}
{{ key }}{% endfor %}
反向编历(reverse)
{% for user in users | reverse %}
{{ user }}{% else %}
没有任何用户{% endfor %}
小知识:通过变量描述当前的状态描述及用法: | loop.index | 当前迭代的索引(从1开始) | | loop.index0 | 当前迭代的索引(从0开始) | | loop.first | 是否是第一次迭代,返回True或False | | loop.last | 是否是最后一次迭代,返回True或False | | loop.length | 序列的长度 | 应用: 对上述的遍历进行添加背景色操作
使用for语句实现九九乘法表
{% for x in range(1,10) %}
{% for y in range(1 , x + 1) %}
{{ y }}*{{ x }} ={{ x*y }}{% endfor %}
{% endfor %}
宏
和Python中的函数类似,可以传递参数,但是不能有返回值,减少代码量。
使用宏和不使用宏的比较
不使用宏:
使用宏的情况
{# 1、定义宏 #}
{% macro input(name,type='text',value ='')%}
{% endmacro %}
{# 2、使用宏 #}
用户名:{{ input('name') }}
密码:{{ input('pwd',type = 'password') }}
{{ input('',type= 'submit',value='提交表单') }}
相比之下,对于上面的简单例子来说,用宏的代码量多一些,但是当代码增多,使用宏就会便捷很多。
但是使用宏的情况,一般不会再模板中直接定义宏,而是会把宏放在一个单独的文件中,这样就需要导入宏了。
宏的导入
方式一:(注意:当使用了【as 宏的别名】时,下面对于宏的使用都要用别名。)from '宏文件的路径' import 宏的名字 [as 宏的别名]
{% from 'index/macros/macro1.html' import input %}
方式二:
import "宏文件的路径" as xxx [with context]
注意:这种方式是导入了一个对象,宏在这个对象中,使用就需要用{{对象名.宏名}}使用。并且也不会共享参数。那么就需要使用with context 让参数共享。
{% import 'index/macros/macro1.html' as inp with context %}
Include标签使用
相当于直接将制定模板中的代码复制粘贴到当前位置。
include的路径,也是跟import一样,直接从templates根目录下去找,不要以相对路径去找。
set+with语句以及模板中定义变量
SET语句
在模板中,可以使用'set'语句来定义变量,并且在后面的都可以使用这个变量。(类似于全局变量)
{% set uname='momo'%}
用户名:{{ uname }}
WITH语句
'with'语句定义的变量,只能在'with'语句块中使用,超过了这个代码块,就不能使用了。(相当于局部变量)
{% with classroom='python202'%}
班级:{{ classroom }}
{% endwith %}案例实现
标志1:{{ flag }}
班长为:{{ monitor }}
啊哈哈:{{ flag }}
{% endwith %}{# with语句块之外能否使用#}班长为:{{ monitor }}
老板是:{{ boss }}
{% endwith %}{# 出了区域#}老板是:{{ boss }}
Flask中加载静态文件
静态文件
静态文件就是CSS JS 图片等文件
加载
加载静态文件使用的是url_for函数,两个参数,第一个参数为固定值‘static’,第二个参数为filename。语法:{{ url_for("static",filename='xxx') }}
CSS加载:
JS加载:
图片加载:(一些简单的样式可以在标签内书写)
模板继承
模板继承就是把公用的代码抽离出来,放到父模板中,让子模板继承使用。
模板继承的语法
使用‘extends’语句,指明父模板的路径,(父模板的路径也是templates文件夹下的绝对路径){% extends "base.html" %}
block语法
因为父模板要有能力提供一个接口,让子模板实现功能代码,从而引出block。
super()函数
默认情况下,子模板如果实现了父模版定义的block。那么子模板block中的代码就会覆盖掉父模板中的代码。如果想要在子模板中仍然保持父模板中的代码,那么可以使用{{ super() }}来实现。
相互调用不同的block
如果想要在另外一个模版中使用其他模版中的代码。那么可以通过{{ self.其他block名字() }}就可以了
注意事项要继承的时候。extends必须在html文件的第一行。
子模板中,如果要实现自己的代码,应该放到block中。如果放到其他地方,那么就不会被渲染。
了解add_url_rule和app.route的原理
add_url_ruleadd_url_rule(rule,endpoint=None,view_func=None)
这个方法用来添加url与视图函数的映射。rule是地址必须有的(如果没有填写endpoint,那么默认会使用view_func的名字作为endpoint)以后在使用url_for的时候,就要看在映射的时候有没有传递endpoint参数,如果传递了,那么就应该使用endpoint指定的字符串,如果没有传递,那么就应该使用view_func的名字。
app.route
app_route的底层原理就是add_url_rule函数。
代码:
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
# 构建url :/list/
# 研究app.add_url_rule()方法,若方法中【没有加】上endpoint时,可通过原来的函数名构建url,即url_for('原函数名')
# print(url_for('my_list'))
# 研究app.add_url_rule()方法,若方法中【加上】endpoint时,不能再通过原来的函数名构建url,而需要endpoint的值才行
# 即url_for('endpoint值')
print(url_for('li'))
return 'Hello World!'
def my_list():
return '啦啦啦啦'
#通过app对象的add_url_rule方法 来完成url与视图函数的映射
app.add_url_rule('/list/',endpoint='li',view_func=my_list)
#讨论:add_url_rule()方法 与@app.route()装饰器的关系
#结论:@app.route()装饰器 底层就是借助于add_url_rule()方法来实现的
app.debug = True
if __name__ == '__main__':
app.run()
类视图
之前我们接触的视图都是函数,所以一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule,view_func)来进行注册。
标准类视图
使用步骤定义一个类,并且必须继承于views.View。而且也必须含有dispatch_request()函数。
注册类视图app.add_url_route('url地址',endpoint = '断点',view_func= '函数名‘)。
代码实现:
from flask import Flask,views,url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
# 定义一个类视图
class ListView(views.View):
def dispatch_request(self):
return '我是列表'
# 注册类视图
app.add_url_rule('/list/',endpoint='mil',view_func=ListView.as_view('my_list'))
# 获取上下文
with app.test_request_context():
#若注册url时,没有指定endpoint,使用as_view()方法中的名称 来构建url
# print(url_for('my_list'))
# # 若注册url时,有指定endpoint,就不能再使用as_view()方法中的名称 来构建url,而要使用endpoint的值来构建url
print(url_for('mil'))
app.debug =True
if __name__ == '__main__':
app.run()
注意:定义类视图,必须继承views.View类和dispatch_request方法。
在app.add_url_rule()函数中,若无指定endpoint,则默认以view_func后的名字构建url,而且要用as_view方法进行转化。
基于调度的类视图
在继承的时候,要继承views.MethodView.
本质
是基于方法的类视图,是根据请求的method来执行不同的方法的。如果用户是发送的get请求,那么将会执行这个类的get方法。如果用户发送的是post请求,那么将会执行这个类的post方法。
作用
这种方式,可以让代码更加简洁。所有和get请求相关的代码都放在get方法中,所有和post请求相关的代码都放在post方法中。就不需要跟之前的函数一样,通过request.method == 'GET'。
基本使用
#定义一个基于方法调度的 类视图
class LoginView(views.MethodView):
def get(self):
return render_template('login.html')
def post(self):
#模拟实现
#拿到前端页面传过来的 账号 和密码 去数据库做查询操作 查询到 (跳转主页面) ,反之跳转到login.html页面并给出错误提示信息
uname = request.form['uname']
pwd = request.form['pwd']
if uname=="momo" and pwd =="123":
return render_template('index.html')
else:
return render_template('login.html',error="用户名或者密码错误")
# 注册类视图
app.add_url_rule('/login/', view_func=LoginView.as_view('my_login'))
但是上述代码是有优化的地方的。
改进1(不过会让代码紊乱,做不到各回各家,各找各妈)
python:
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=="momo" and pwd =="123":
return render_template('index.html')
else:
return self.get(error="用户名或者密码错误")
# render_template('login.html',error="用户名或者密码错误")
# 注册类视图
app.add_url_rule('/login/', view_func=LoginView.as_view('my_login'))
Jinja2:
{# {{ error }}#}
{# 优化,判断#}
{% if error %} {#None == False 不为None或者空的字符串和列表时,才为True#}
{{ error }}
{% endif %}
改进2(最终写法)
# 改进2:基于调度方法的类视图 ,通常get()方法处理get请求,post()方法处理post请求,为了便于管理,不推荐post方法和get方法互相调用
class LoginView(views.MethodView):
def __jump(self,error=None):
return render_template('login.html', error=error)
def get(self,error = None):
return self.__jump()
def post(self):
#模拟实现
#拿到前端页面传过来的 账号 和密码 去数据库做查询操作 查询到 (跳转主页面) ,反之跳转到login.html页面并给出错误提示信息
uname = request.form['uname']
pwd = request.form['pwd']
if uname=="momo" and pwd =="123":
return render_template('index.html')
else:
return self.__jump(error="用户名或者密码错误")
类视图的优点:可继承
这一优点可以减少代码量,比如说两个子类都需要把字典对象转化为json对象,就可以通过继承来实现。
代码:
class ListView2(views.View):
def getDate(self):
raise NotImplementedError
def dispatch_request(self):
return jsonify(self.getDate())
# 重写了父类的getDate()方法
class JSONView1(ListView2):
def getDate(self):
return {'name':'张善峰','age':123}
class JSONView2(ListView2):
def getDate(self):
return {'book':'你好','author':'我'}
app.add_url_rule('/people/',endpoint='people',view_func=JSONView1.as_view('JSONView1'))
app.add_url_rule('/book/',endpoint='book',view_func=JSONView2.as_view('JSONView2'))
类视图中的装饰器
装饰器
是用于拓展原来函数功能的函数,它的返回值也是一个函数,可以在不增加其源代码的前提下增加功能。
在视图函数中使用装饰器
# 定义一个装饰器
#需求:查看设置个人信息时,只有检测到用户已经登录了才能查看,若没有登录,则无法查看并给出提示信息
def login_required(func):
# 装饰器必须要有的
@wraps(func)
def wrapper(*args,**kwargs):
username = request.args.get("username")
if username and username == 'momo':
return func(*args,**kwargs)
else:
return '请先登录'
return wrapper
# 功能函数 能够执行成功的前提条件是, 必须先登录【写代码去判断当前用户是否已经登录--》放到自定义装饰器】
@app.route('/setting/')
# 在视图函数中使用自定义装饰器,那么自己定义的装饰器必须放在`app.route`下面。否则这个装饰器就起不到任何作用。
@login_required
def setting():
return '这是设置界面'
在类视图中使用装饰器
在类视图中使用装饰器,是需要重写类视图中的decorators属性,并且把装饰器加到属性中,列表、元组都可以。
class ProfileView(views.View):
# 列表或者元组都行
decorators = [login_required]
def dispatch_request(self):
return '这是个人中心界面'
app.add_url_rule('/profile/',view_func=ProfileView.as_view('profile'))
蓝图(Blueprint)
简介
让Flask更加模块化,更好的管理项目,分层解耦(mvc结构)
基本使用
使用的步骤分为两个步骤导入:在蓝图文件中导入Blueprint from flask import Blueprint users = Blueprint('users',name,url_prefix='/user')
注册:在主py文件中执行蓝图文件中的调用 #(先引入 ‘.’=/) from blueprints.book import books # (在注册) app.register_blueprint(users)
url_prefix参数
如果想要某个蓝图下的所有url都有一个url前缀,那么可以在定义蓝图的时候,指定url_prefix参数,同时也要注意:定义url_profix时,若后边有/,那么以后在定义url与视图函数的时候,就不要再在url前面加斜杠了。
模板文件
寻找规则如果项目中的templates文件夹中有相应的模版文件,就直接使用了。
如果项目中的templates文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。比如:news_bp = Blueprint('news',name,url_prefix='/news',template_folder='news_page')
注意
蓝图文件查找会默认在template为根目录查找。优先级是template目录要大于自定义目录,所以要想自定义先把template目录中的文件删除。
静态文件
寻找规则
常规写法(必须掌握)
个性化写法(了解)
如果在加载静态文件的时候,指定的蓝图的名字,比如news.static,那么就会到这个蓝图指定的static_folder下查找静态文件。
python:
from flask import Blueprint
news_bp=Blueprint('news',__name__,url_prefix='/news',template_folder='news_page',static_folder='news_page_static')
html
url_for反转蓝图注意事项
注意:要指定蓝图的名字。
场景一
url_for (‘蓝图名称.方法名')
场景二
新闻列表 OK写法
场景三
from flask import Blueprint,render_template,url_for
news_bp = Blueprint('news',__name__,url_prefix='/news',template_folder='news_page',static_folder='news_page_static')
@news_bp.route('/list/')
def news_list():
print(url_for('news.news_detail')) #/news/detail/
return render_template('news_list.html')
蓝图_子域名实现ip地址不能有子域名。
localhost也不能有子域名。
分为四个部分,蓝图文件,默认主页,子域名主页,主PY文件
蓝图文件
from flask import Blueprint,render_template
cms_bp = Blueprint('cms',__name__,subdomain='cms')
#子域名的首页
@cms_bp.route('/')
def hello():
return render_template('cms_index.html')
默认主页
ko
ko
ok
学院
子域名主页
学院
主py文件
from blueprints.cms import cms_bp
app.register_blueprint(cms_bp)
app.config['SERVER_NAME']="momo.com:5000"