`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(rule,**options)`装饰器:
这个装饰器底层,其实也是使用`add_url_rule`来实现url与视图函数映射的。
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/',endpoint='hello')
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()方法来实现的
#请求上下文对象 项目一启动就会执行
with app.test_request_context():
# print(url_for('hello_world'))
print(url_for('hello'))
if __name__ == '__main__':
app.run(debug=True)
flask.views.View
.dipatch_request
方法,以后请求过来后,都会执行这个方法。这个方法的返回值就相当于是之前的函数视图一样。也必须返回Response
或者子类的对象,或者是字符串,或者是元组。app.add_url_rule(rule,endpoint,view_func)
来做url与视图的映射。view_func
这个参数,需要使用类视图下的as_view
类方法类转换:ListView.as_view('list')
。endpoint
,那么在使用url_for
反转的时候就必须使用endpoint
指定的那个值。如果没有指定endpoint
,那么就可以使用as_view(视图名字)
中指定的视图名字来作为反转。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 "这是List列表"
#注册类视图
app.add_url_rule('/list/',endpoint='mlist',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('mlist'))
类视图的好处
1,可以继承,把一些共性的东西抽取出来放到父视图中,子视图直接拿来用就可以了。
2,但是也不是说所有的视图都要使用类视图,这个要根据情况而定。视图函数用得最多
#类视图的好处:
#需求:以后有 好几个url,都需要返回json对象的格式
class ListView2(views.View):
def getData(self):
raise NotImplementedError
def dispatch_request(self):
return jsonify(self.getData())
class JSONView(ListView2):
def getData(self):
return {'uname':'momo','age':'22'}
class JSONView2(ListView2):
def getData(self):
return {'bname':'水浒传','price':'89'}
app.add_url_rule('/json/',view_func=JSONView.as_view('my_json'))
app.add_url_rule('/json2/',view_func=JSONView2.as_view('my_json2'))
标准类视图使用场景
#需求2:有好几个url,跳转到到不同的页面时,会带一个相同的参数过去
# #登录功能
# class LoginView(views.View):
# def dispatch_request(self):
# return render_template('login.html',ads="茅台酒 998")
#
# #注册功能
# class RegisterView(views.View):
# def dispatch_request(self):
# return render_template('register.html',ads="茅台酒 998")
#改进:突出类视图的好处
class ADSView(views.View):
def __init__(self):
super(ADSView, self).__init__()
self.context={
'ads':'华为 5G'
}
#登录功能
class LoginView(ADSView):
def dispatch_request(self):
self.context.update({'pid':'好牛的一本书'})
return render_template('login.html',**self.context)
#注册功能
class RegisterView(ADSView):
def dispatch_request(self):
return render_template('register.html',**self.context)
app.add_url_rule('/login/',view_func=LoginView.as_view('login'))
app.add_url_rule('/register/',view_func=RegisterView.as_view('register'))
1,基于方法的类视图,是根据请求的`method`来执行不同的方法的。
如果用户是发送的`get`请求,那么将会执行这个类的`get`方法。
如果用户发送的是`post`请求,那么将会执行这个类的`post`方法。其他的method类似,比如`delete`、`put`。
2,这种方式,可以让代码更加简洁。所有和`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:
#改进1
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="用户名或者密码错误")
# 注册类视图
app.add_url_rule('/login/', view_func=LoginView.as_view('my_login'))
html页面
改进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="用户名或者密码错误")
# 注册类视图
app.add_url_rule('/login/', view_func=LoginView.as_view('my_login'))
python装饰器就是用于拓展【原来函数功能】的一种函数,这个【函数的特殊之处在于它的返回值也是一个函数】,
使用python装饰器的 【好处】就是在不用更改原函数的代码前提下给函数增加新的功能。
1. 视图函数中使用自定义装饰器,那么自己定义的装饰器必须放在`app.route`下面。否则这个装饰器就起不到任何作用。
例:
定义一个装饰器
#需求:查看设置个人信息时,只有检测到用户已经登录了才能查看,若没有登录,则无法查看并给出提示信息
def login_requierd(func):
@wraps(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('/settings/')
@login_requierd
def settings():
return '这是设置界面'
2. 在类视图中使用装饰器,需要重写类视图的一个类属性`decorators`,
3. 这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。
定义一个装饰器
#需求:查看设置个人信息时,只有检测到用户已经登录了才能查看,若没有登录,则无法查看并给出提示信息
def login_requierd(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
使用自定义装饰器:
class ProfileView(views.View):
decorators = [login_requierd]
def dispatch_request(self):
return '这是个人中心界面'
app.add_url_rule('/profile/',view_func=ProfileView.as_view('profile'))
之前我们写的url和视图函数都是处在同一个文件,如果项目比较大的话,这显然不是一个合理的结构,
而蓝图可以优雅的帮我们实现这种需求。
1. 蓝图的作用就是让我们的Flask项目更加模块化,结构更加清晰,为了更好的管理项目
让项目达到分层解耦 而产生的。可以将相同模块的视图函数放在同一个蓝图下,同一个文件中,方便管理。
3. 基本语法:
* 在蓝图文件中导入Blueprint:
from flask import Blueprint
user_bp = Blueprint('user',__name__)
* 在主app文件中注册蓝图:
from flask import Flask
from blueprints.user import user_bp
app = Flask(__name__)
app.register_blueprint(user_bp)
3. 如果想要某个蓝图下的所有url都有一个url前缀,那么可以在定义蓝图的时候,指定url_prefix参数:
user_bp = Blueprint('user',__name__,url_prefix='/user')
#个人中心的 url与视图函数
@user_bp.route('/profile/')
def profile():
return '个人中心页面'
#个人设置中心的 url与视图函数
@user_bp.route('/settings/')
def settings():
return '个人设置页面'
注意: 在定义url_prefix的时候,要注意后面的斜杠,如果给了,那么以后在定义url与视图函数的时候,
就不要再在url前面加斜杠了。
蓝图中模版文件寻找规则
4. 蓝图模版文件的查找:
* 如果项目中的templates文件夹中有相应的模版文件,就直接使用了。
* 如果项目中的templates文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。比如:
news_bp = Blueprint(‘news’,name,url_prefix=’/news’,template_folder=‘news_page’)
项目截图为:
因为这个蓝图文件是在blueprints/news.py,那么就会到blueprints这个文件夹下的news_page文件夹中寻找模版文件。
小总结:
常规:蓝图文件在 查找模版文件时,会以templates为根目录进行查找
news_bp = Blueprint(‘news’,name,url_prefix=’/news’)
注意1:个性化coder 喜欢在【创建蓝图对象的时候】 指定 模版文件的查找路径 如下:
news_bp = Blueprint(‘news’,name,url_prefix=’/news’,template_folder=‘news_page’)
注意2:只有确定templates目录下没有对应的 html文件名的时候,才会去蓝图文件指定的目录下查找,指定才会生效
注意3:若templates目录下,有一个与蓝图文件指定的目录下同名的一个 html文件时,优先走templates目录下的东西
html:
news.static
,那么就会到这个蓝图指定的static_folder下查找静态文件。如:python:
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')
html:
寻找结果如图:
小总结:
蓝图文件查找方式1【掌握】:查找静态文件时,正常情况下,会以static为根目录进行查找
蓝图文件查找方式2【了解】:查找静态文件时,非正常情况下,需要用url_for(‘蓝图的名字.static’),
然后会去蓝图对象在创建时指定的静态文件夹目录下 去查找静态文件
news_bp = Blueprint(‘news’,name,url_prefix=’/news’,template_folder=‘news_page’,static_folder=‘news_statics’)
url_for反转蓝图中的视图函数为url:
如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,那么就应该在使用url_for的时候指定这个蓝图名字。
app类中、模版中、同一个蓝图类中都是如此。否则就找不到这个endpoint。
如app类 blueprint_demo.py中:
#如下写法:才找得到 url_for('蓝图名称.方法名')
print(url_for('news.news_list'))#/news/list/
如模版/templates/index.html中:
新闻列表 OK写法
{# 新闻列表 no Ok写法#}
如同一个蓝图类/blueprints/news.py中:
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')
@news_bp.route('/detail/')
def news_detail():
return '新闻详情页面'
蓝图实现子域名:
1. 使用蓝图技术。
2. 在创建蓝图对象的时候,需要传递一个`subdomain`参数,来指定这个子域名的前缀。
例如:cms_bp= Blueprint('cms',__name__,subdomain='cms')
3. 需要在主app文件中,需要配置app.config的SERVER_NAME参数。例如:
app.config['SERVER_NAME']='momo.com:5000'
4. 在`C:\Windows\System32\drivers\etc`下,找到hosts文件,然后添加域名与本机的映射。
域名和子域名都需要做映射。例如:
127.0.0.1 momo.com
127.0.0.1 cms.momo.com
注意:
* ip地址不能有子域名。
* localhost也不能有子域名。