flask add_url_rule和app.route原理剖析 类视图 蓝图

1,add_url_rule和app.route原理剖析

`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)

2,类视图_标准类视图使用步骤

  1. 标准类视图,必须继承自flask.views.View.
  2. 必须实现dipatch_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,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'))

3,类视图的好处_支持继承以及标准类视图使用场景

类视图的好处

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'))

标准类视图使用场景

在这里插入图片描述
flask add_url_rule和app.route原理剖析 类视图 蓝图_第1张图片
flask add_url_rule和app.route原理剖析 类视图 蓝图_第2张图片

#需求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'))

4,类视图_基于调度方法的类视图

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页面
账号:
密码:
{# {{ error }}#} {# 优化写法 :判断 #} {% if error %} {{ 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="用户名或者密码错误") # 注册类视图 app.add_url_rule('/login/', view_func=LoginView.as_view('my_login'))

5,类视图_类视图中使用装饰器

 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'))

6,蓝图简介与基本使用

 之前我们写的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前面加斜杠了。

访问结果如下:
在这里插入图片描述

7,蓝图中模版文件寻找规则

蓝图中模版文件寻找规则
4. 蓝图模版文件的查找:
* 如果项目中的templates文件夹中有相应的模版文件,就直接使用了。
* 如果项目中的templates文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。比如:
news_bp = Blueprint(‘news’,name,url_prefix=’/news’,template_folder=‘news_page’)
项目截图为:
flask add_url_rule和app.route原理剖析 类视图 蓝图_第3张图片
因为这个蓝图文件是在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目录下的东西

8,蓝图中静态文件寻找规则

  • 在模版文件中,加载静态文件,如果使用url_for(‘static’),那么就只会在app指定的静态文件夹目录下查找 静态文件。如:
html:

寻找结果如图:
flask add_url_rule和app.route原理剖析 类视图 蓝图_第4张图片

  • 如果在加载静态文件的时候,指定的蓝图的名字,比如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:

寻找结果如图:
flask add_url_rule和app.route原理剖析 类视图 蓝图_第5张图片
小总结:
蓝图文件查找方式1【掌握】:查找静态文件时,正常情况下,会以static为根目录进行查找

蓝图文件查找方式2【了解】:查找静态文件时,非正常情况下,需要用url_for(‘蓝图的名字.static’),
然后会去蓝图对象在创建时指定的静态文件夹目录下 去查找静态文件
news_bp = Blueprint(‘news’,name,url_prefix=’/news’,template_folder=‘news_page’,static_folder=‘news_statics’)

9,url_for反转蓝图注意事项

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 '新闻详情页面'

10,蓝图_子域名实现详解

蓝图实现子域名:
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也不能有子域名。

你可能感兴趣的:(Flask,flask,add_url_rule,flask,app.route,flask,类视图,flask,蓝图)