Flask学习之旅 --- 高级篇

Flask视图函数高级

1.1 add_url_rule的用法

add_url_rule(rule,endpoint=None,view_func=None) 这个方法用来添加url与视图函数的映射。如果没有填写endpoint,那么默认会使用view_func的名字作为endpoint。以后在使用url_for的时候,就要看在映射的时候有没有传递endpoint参数,如果传递了,那么就应该使用endpoint指定的字符串,如果没有传递,那么就应该使用view_func的名字。

  • flask代码
from flask import Flask,url_for

app = Flask(__name__)


@app.route('/',endpoint='index')
def hello_world():
    print(url_for('python'))
    return 'Hello World!'

def my_list():
    return '我是列表页'

app.add_url_rule('/list/',endpoint='python',view_func=my_list)

# app.test_request_context
# 请求上下文
with app.test_request_context():
    print(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)

1.2 app.route装饰器的用法

app.route(rule,**options)装饰器: 这个装饰器底层,其实也是使用add_url_rule来实现url与视图函数映射的。

from flask import Flask,url_for

app = Flask(__name__)


@app.route('/',endpoint='index')
def hello_world():
    print(url_for('python'))
    return 'Hello World!'

# app.test_request_context
# 请求上下文
with app.test_request_context():
    print(url_for('index'))
    

if __name__ == '__main__':
    app.run(debug=True)

1.3 类视图

1.3.1 标准类视图

  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(视图名字)中指定的视图名字来作为反转。
  5. 类视图有以下好处:可以继承,把一些共性的东西抽取出来放到父视图中,子视图直接拿来用就可以了。但是也不是说所有的视图都要使用类视图,这个要根据情况而定。

flask代码:

from flask import Flask,views,url_for,jsonify,render_template

app = Flask(__name__)

# 有几个url需要返回json数据
# 有几个视图,是需要返回相同的变量

class JSONView(views.View):
    def get_data(self):
        raise NotImplementedError

    def dispatch_request(self):
        return jsonify(self.get_data())

class ListView(JSONView):
    def get_data(self):
        return {"username":'xiaoming','password':'111111'}

app.add_url_rule('/list/',endpoint='my_list',view_func=ListView.as_view('list'))


class ADSView(views.View):
    def __init__(self):
        super(ADSView, self).__init__()
        self.context = {
            'ads': '今年过节不收礼'
        }

class LoginView(ADSView):
    def dispatch_request(self):
        self.context.update({
            'username': 'xiaoming'
        })
        return render_template('login.html',**self.context)

class RegistView(ADSView):
    def dispatch_request(self):
        return render_template('regist.html',**self.context)

app.add_url_rule('/login/',view_func=LoginView.as_view('login'))
app.add_url_rule('/regist/',view_func=RegistView.as_view('regist'))


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run(debug=True)

login.html代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    这是登录页面
    <p>{{ ads }}</p>
</body>
</html>

regist.html代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    这是注册页面
    <p>{{ ads }}</p>
</body>
</html>

1.3.2 基于请求方法的类视图

  1. 基于方法的类视图,是根据请求的method来执行不同的方法的。如果用户是发送的get请求,那么将会执行这个类的get方法。如果用户发送的是post请求,那么将会执行这个类的post方法。其他的method类似,比如deleteput
  2. 这种方式,可以让代码更加简洁。所有和get请求相关的代码都放在get方法中,所有和post请求相关的代码都放在post方法中。就不需要跟之前的函数一样,通过request.method == 'GET'

flask代码:

from flask import Flask,views,render_template,request

app = Flask(__name__)


@app.route('/')
def hello_world():
    if request.method == 'GET':
        return 'Hello World!'
    else:
        # 写一些post请求的代码
        pass

class LoginView(views.MethodView):
    def __render(self,error=None):
        return render_template('login.html',error=error)

    def get(self):
        return self.__render()


    def post(self):
        username = request.form.get('username')
        password = request.form.get('password')
        if username == 'zhiliao' and password == '111111':
            return '登录成功'
        else:
            return self.__render(error='用户名或密码错误')

app.add_url_rule('/login/',view_func=LoginView.as_view('login'))


if __name__ == '__main__':
    app.run(debug=True)

index.html代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <table>
        <tbody>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="立即登录"></td>
            </tr>
        </tbody>
    </table>
    {% if error %}
        <p style="color: red;">{{ error }}</p>
    {% endif %}
</form>
</body>
</html>

1.3.3 类视图中的装饰器

  1. 如果使用的是函数视图,那么自己定义的装饰器必须放在app.route下面。否则这个装饰器就起不到任何作用。
  2. 类视图的装饰器,需要重写类视图的一个类属性decorators,这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。

flask代码:

from flask import Flask,request,views
from functools import wraps

app = Flask(__name__)

def login_required(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        # /settings/?username=xxx
        username = request.args.get('username')
        if username and username == 'zhiliao':
            return func(*args,**kwargs)
        else:
            return '请先登录'
    return wrapper

@app.route('/')
def hello_world():
    return 'Hello World!'

@app.route('/settings/')
@login_required
def settings():
    return '这是设置界面'

# login_required(app.route('/settings/')(settings))
#
# app.route('/settings/')(login_required(settings))


class ProfileView(views.View):
    decorators = [login_required]
    def dispatch_request(self):
        return '这是个人中心界面'

app.add_url_rule('/profile/',view_func=ProfileView.as_view('profile'))


if __name__ == '__main__':
    app.run(debug=True)

1.4 蓝图

1.4.1 蓝图的使用

  1. 蓝图的作用就是让我们的Flask项目更加模块化,结构更加清晰。可以将相同模块的视图函数放在同一个蓝图下,同一个文件中,方便管理。

  2. 基本语法:

    • 在蓝图文件中导入Blueprint
      from flask import Blueprint
      user_bp = Blueprint('user',__name__)
      
    • 在主app文件中注册蓝图:
      from blueprints.user import user_bp
      app.regist_blueprint(user_bp)
      
  3. 如果想要某个蓝图下的所有url都有一个url前缀,那么可以在定义蓝图的时候,指定url_prefix参数:

    user_bp = Blueprint('user',__name__,url_prefix='/user/')
    

    在定义url_prefix的时候,要注意后面的斜杠,如果给了,那么以后在定义url与视图函数的时候,就不要再在url前面加斜杠了。

  4. 蓝图模版文件的查找:

    • 如果项目中的templates文件夹中有相应的模版文件,就直接使用了。
    • 如果项目中的templates文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。比如:
      news_bp = Blueprint('news',__name__,url_prefix='/news',template_folder='muban')
      
      因为这个蓝图文件是在blueprints/news.py,那么就会到blueprints这个文件夹下的muban文件夹中寻找模版文件。
  5. 蓝图中静态文件的查找规则:

    • 在模版文件中,加载静态文件,如果使用url_for('static'),那么就只会在app指定的静态文件夹目录下查找静态文件。
    • 如果在加载静态文件的时候,指定的蓝图的名字,比如news.static,那么就会到这个蓝图指定的static_folder下查找静态文件。
  6. url_for反转蓝图中的视图函数为url

    • 如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,那么就应该在使用url_for的时候指定这个蓝图。比如news.news_list。否则就找不到这个endpoint。在模版中的url_for同样也是要满足这个条件,就是指定蓝图的名字。
    • 即使在同一个蓝图中反转视图函数,也要指定蓝图的名字。

1.4.2 蓝图实现子域名

  1. 使用蓝图技术。
  2. 在创建蓝图对象的时候,需要传递一个subdomain参数,来指定这个子域名的前缀。例如:cms_bp = Blueprint('cms',__name__,subdomain='cms')
  3. 需要在主app文件中,需要配置app.config的SERVER_NAME参数。例如:
    app.config['SERVER_NAME'] = 'jd.com:5000'
    
    • ip地址不能有子域名。
    • localhost也不能有子域名。
  4. C:\Windows\System32\drivers\etc下,找到hosts文件,然后添加域名与本机的映射。例如:
    127.0.0.1   jd.com
    127.0.0.1   cms.jd.com
    
    域名和子域名都需要做映射。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350