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

本文深入解析Flask框架中路由与视图的高级应用,包括add_url_rule和app.route的区别,类视图的实现与优势,以及蓝图的使用技巧。详细介绍了如何通过装饰器增强视图功能,蓝图的静态与模板文件管理,和子域名的配置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

标准类视图使用场景

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#需求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页面
<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>

改进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’)
项目截图为:
在这里插入图片描述
因为这个蓝图文件是在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:
<link rel="stylesheet" href="{{ url_for('static',filename='news_list.css') }}">

寻找结果如图:
在这里插入图片描述

  • 如果在加载静态文件的时候,指定的蓝图的名字,比如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:
<link rel="stylesheet" href="{{ url_for('news.static',filename='news_list.css') }}">

寻找结果如图:
在这里插入图片描述
小总结:
蓝图文件查找方式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中:
      <a href="{{ url_for('news.news_list')}}">新闻列表 OK写法</a>
   {# <a href="{{ url_for('news_list')}}">新闻列表 no Ok写法</a>#}

    如同一个蓝图类/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也不能有子域名。
<think>我们注意到用户之前尝试安装Flask,但遇到了"Requirement already satisfied"的提示,现在用户可能遇到了运行Flask应用时的错误(“爆红”)。根据引用[5],我们可以知道在开发时通常需要开启debug模式,以便在代码改动时自动重启并提供详细的错误信息。此外,用户可能没有正确设置路由或应用实例。 然而,用户提供的引用中,引用[1]展示了如何在一个应用中运行多个Flask实例(使用DispatcherMiddleware),引用[2]提到了Flask-Script扩展(用于添加命令行选项),引用[3]是关于添加WSGI中间件,引用[4]是关于异常处理,引用[5]则提到了debug模式基本路由。 用户的问题可能是由于多种原因导致的,比如: 1. 应用实例没有正确创建。 2. 路由定义错误。 3. 没有正确运行应用(比如忘记调用app.run())。 4. 端口被占用等。 由于用户提到“爆红”,通常意味着在IDE(如PyCharm)中运行时代码有错误(红色错误提示)。因此,我们需要提供一些基本的排查步骤。 下面我将提供一份详细的排查指南,帮助用户解决运行简单Flask应用时出现的错误。 ### 步骤1: 确保最基本的Flask应用结构正确 一个最简单的Flask应用应该包含以下部分: ```python from flask import Flask app = Flask(__name__) # 创建Flask应用实例 @app.route('/') # 定义路由 def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run(debug=True) # 在调试模式下运行应用[^5] ``` ### 步骤2: 检查常见错误 - **错误1: 没有创建Flask实例或变量名错误** 确保变量名`app`与运行时的`app.run()`中的`app`一致。如果不一致,比如你创建的是`myapp = Flask(__name__)`,那么运行时应为`myapp.run()`。 - **错误2: 路由定义的位置错误** 确保路由装饰器`@app.route()`紧挨着视图函数上方,中间不能有空行或其他代码。 - **错误3: 忘记启动应用** 确保在文件末尾有`app.run(debug=True)`,并且只有在直接运行该文件时才执行(即`if __name__ == '__main__':`条件满足)。 - **错误4: 端口被占用** 默认端口是5000,如果被占用,可以尝试更换端口: ```python app.run(port=5001, debug=True) ``` ### 步骤3: 开启调试模式 在开发时,开启调试模式(debug=True)非常重要,因为: - 它会在代码修改后自动重启服务器。 - 当发生错误时,会提供一个详细的错误页面,帮助定位问题[^5]。 ### 步骤4: 查看错误日志 如果运行后出现错误,控制台会输出错误信息(通常是红色)。根据错误信息进行排查。常见的错误包括: - **ImportError: No module named 'flask'** 说明Flask没有正确安装。请确保在正确的虚拟环境中,并已激活。如果使用PyCharm,请检查项目解释器设置。 - **Address already in use** 端口被占用,更换端口。 - **TypeError: __init__() got an unexpected keyword argument 'debug'** 可能将`debug`参数写在了`Flask`类的实例化中,而不是`run`方法中。正确做法是`app.run(debug=True)`。 - **404 Not Found** 检查访问的URL是否与定义的路由匹配。 ### 步骤5: 使用Flask命令行运行(可选) 除了在代码中调用`app.run()`,还可以使用Flask命令行工具: ```bash export FLASK_APP=your_application.py # Linux/Mac # 或者 set FLASK_APP=your_application.py # Windows flask run --debug ``` 注意:`your_application.py`是你的主应用文件名。 ### 示例:一个完整的可运行的最小应用 ```python # app.py from flask import Flask app = Flask(__name__) @app.route('/') def home(): return "Hello, Flask World!" if __name__ == '__main__': app.run(debug=True, port=5000) # 设置端口调试模式[^5] ``` ### 运行结果 如果一切正常,控制台会输出类似以下信息: ``` * Serving Flask app 'app' * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) * Restarting with stat * Debugger is active! ``` ### 如果问题仍然存在 如果按照上述步骤仍然无法解决,请提供具体的错误信息(红色错误提示),以便进一步分析。 §§相关问题§§ 1. 如何配置Flask应用的调试模式? 2. 如何解决Flask运行时的端口冲突问题? 3. Flask中的路由装饰器是如何工作的? 4. 如何为Flask应用设置不同的环境配置(如开发、生产)? [^1]: 使用DispatcherMiddleware可以运行多个Flask应用。 [^2]: Flask-Script扩展提供了命令行管理功能。 [^3]: 可以通过覆盖wsgi_app属性来添加WSGI中间件。 [^4]: 使用errorhandler可以捕获特定异常并处理。 [^5]: 开启debug模式可以自动重启服务器并提供详细错误信息。</think>### 解决 Flask 应用运行错误的完整指南 当运行简单的 Flask 应用时出现错误(通常称为"爆红"),这通常是由于配置问题或代码结构错误导致的。以下是系统化的解决方案: #### 1. 确认基本应用结构正确 确保你的 Flask 应用符合基本结构: ```python from flask import Flask app = Flask(__name__) # 必须正确实例化Flask对象[^5] @app.route('/') # 正确使用路由装饰器[^5] def hello(): return "Hello, Flask World!" # 确保视图函数有返回值 if __name__ == '__main__': app.run(debug=True) # 开启调试模式[^5] ``` #### 2. 常见错误及解决方案 | 错误类型 | 原因 | 解决方案 | |---------|------|---------| | **ImportError** | 未正确安装Flask或环境问题 | `pip install flask` 并确认使用虚拟环境 | | **RuntimeError** | 未设置FLASK_APP环境变量 | 设置环境变量:`export FLASK_APP=your_app.py` | | **端口冲突** | 默认端口5000被占用 | 更换端口:`app.run(port=5001)` | | **路由错误** | 路由定义不正确 | 检查`@app.route()`装饰器的位置语法[^5] | | **WSGI错误** | 中间件配置问题 | 检查WSGI中间件配置[^3] | #### 3. 调试模式开启方法 在开发阶段务必开启调试模式,它能提供详细错误信息: ```python # 方法1 app.debug = True app.run() # 方法2(推荐) app.run(debug=True) # 提供自动重载详细错误页面[^5] ``` #### 4. 错误处理最佳实践 使用Flask的错误处理钩子捕获异常: ```python @app.errorhandler(404) # 处理404错误 def page_not_found(error): return "页面不存在", 404 @app.errorhandler(500) # 处理服务器错误 def internal_error(error): return "服务器内部错误", 500 ``` 对于特定异常(如数据库连接错误),可注册自定义处理器[^4]。 #### 5. 多应用场景下的特殊处理 如果需要运行多个Flask应用(如引用[1]所示),使用DispatcherMiddleware: ```python from werkzeug.middleware.dispatcher import DispatcherMiddleware from werkzeug.serving import run_simple app1 = Flask('app1') app2 = Flask('app2') dm = DispatcherMiddleware(app1, {'/app2': app2}) run_simple('localhost', 5000, dm) # 运行多个应用[^1] ``` #### 6. 使用Flask-Script扩展 对于复杂应用,使用Flask-Script管理命令行: ```python from flask_script import Manager manager = Manager(app) # 初始化Manager[^2] @manager.command def custom_command(): print("执行自定义命令") if __name__ == '__main__': manager.run() # 替代app.run()[^2] ``` #### 7. 测试应用是否正常运行 创建测试路由验证基本功能: ```python @app.route('/healthcheck') def healthcheck(): return jsonify(status="OK", version="1.0"), 200 ``` 访问 `http://localhost:5000/healthcheck` 应返回JSON响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值