# 模板、请求与响应

# jinja2语法

  • Flask使用的是Jinja2模板,所以其语法和Django基本无差别,因为templete语法是继承jinjia2

# 1、模板基本数据的渲染

变量 {{..}}
列表 {% for item in List %}<li>{{item}}</li>{% endfor %}
字典 {% for k,v in Dict.items() %}<li>{{k}}{{v}}</li>{% endfor %}

# 2 注意:Markup等价django的mark_safe 用于返回有效的html标签

也可以像django一样使用 {{data|safe}}


>>> from flask import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
>>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
u'Marked up \xbb HTML'

# 3 像django的simple_tag, filter 一样,传递自定义的函数

局部 -- 局部模板使用
    1、局部函数 -- 只能在传入的template中使用,直接定义,传递,在视图函数中定义
      def add_num(a,b):
        return int(a) + int(b)

    2、在视图函数中返回时:
        return render_template('..html','add_num':add_num)

    3、模板中使用:
        {{add_num(1,2)}}

-------------------------------------------------
全局  -- 全局使用

    django 的 filter可以当做if条件
    需要 @app.template_global()装饰器   
         def func(a,b,c)  --->>  全局temlate使用 {{func(a,b,c)}}

      和 @app.template_filter()装饰器
         def func(a,b,c)  --->>  全局temlate使用 {{a|filter(b,c)}}  # 需要管道符

# 4 模板继承

{% extends 'layout.html' %}  #模板的继承

{% block body %}   #块继承  
{% endblock %}

{% include 'asd.html' %}   #组件的使用

# 5 定义宏: 相当于定义函数来控制html的内容

一般是用于多次会用到的地方,比如分页;相当于定义了个函数,用的时候调用才行

在需要重用的html中写,paginator.html:

{% macro page(data,url) -%}

    {% if data %}
        <ul class="pagination pagination-sm no-margin">
            <li><a href="{{ url_for(url,page=1) }}">首页</a></li>

            {% if data.has_prev %}
            <li><a href="{{ url_for(url,page=data.prev_num) }}">上一页</a></li>
            {% else %}
            <li class="disabled"><a href="">上一页</a></li>
            {% endif %}

            {% for v in data.iter_pages() %}
                {% if v == data.page %}
                <li class="active"><a href="{{ url_for(url,page=v) }}">{{ v }}</a></li>
                {% else %}
                <li><a href="{{ url_for(url,page=v) }}">{{ v }}</a></li>
                {% endif %}

            {% endfor %}


            {% if data.has_next %}
            <li><a href="{{ url_for(url,page=data.next_num) }}">下一页</a></li>
            {% else %}
            <li class="disabled"><a href="">下一页</a></li>
            {% endif %}
            <li><a href="{{ url_for(url,page=data.pages) }}">尾页</a></li>
        </ul>
    {% endif %}

{%- endmacro  %}    

调用:

{% import 'admin/paginator.html' as pg %}
{{ pg.page(page_data,'admin.tag_list') }}

函数的例子:

视图中:

from flask import Flask,redirect,Markup,render_template,make_response
app = Flask(__name__)

    # 全局使用
@app.template_filter()
def Filter(a,b,c):
    return min(a,b,c)

    # 全局使用
@app.template_global()
def Global(a,b,c):
    return a + b + c

    # 自定义函数 -- 局部
def add_num(a,b):
    return int(a) + int(b)

@app.route('/index',endpoint='index',methods=['GET'])
def index():

    Dict = {
        'List':[1,2,3],
        'Dict':{'amy':18,'bob':20},
        'Str':'hello',
        'add_num':add_num,
        'tag':Markup('<a href="#">点击</a>')  # 或者在templates中使用 |safe
    }

    response = make_response(render_template('index.html',**Dict))
    return response

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

html中:

<p>hello!</p>

<p>字符串:{{Str}}</p>

<ul>
列表:
    {% for item in List %}
    <li>
        {{item}}
    </li>
    {% endfor %}
字典:
    {% for k,v in Dict.items() %}
    <li>
        {{k}}{{v}}
    </li>
    {% endfor %}
</ul>

<p>自定义加法函数:{{add_num(1,4)}}</p>
<p>全局函数global:{{Global(1,4,5)}}</p>
<p>全局函数filter:{{1|Filter(1,4)}}</p>


html标签:{{tag}}
  • Flask中模板使用函数的两种方法

    from flask import Flask, render_template,Markup
    
    app = Flask(import_name=__name__)
    
    def func1(args):
        return Markup("<input type='text' value='%s' />" % (args,))
    
    @app.route('/index1')
    def index3():
        return render_template('test.html',ff=func1)
    
    if __name__ == '__main__':
        app.run()
        
     #html文件中
    <body>
        <!-- 方式一-->
        {{ff(12)|safe}}
        <!--方式二-->
        {{ff(12)}}
    </body>
    

# Flask中的Response相关

   Django的响应                 Flask响应
1.HTTPResponse("hello") 	""   返回str
2.render 响应模板			 render_template("html")  返回str
3.redirect("/")				redirect("/")	返回str
				   以上是Web框架的 Response 三剑客

4.send_file()	 instance     返回文件内容,自动识别文件类型,Content-type中添加文件类型,Content-type:文件类型
 #浏览器特性 可识别的Content-type 自动渲染 不可识别的Content-type 会自动下载

5.jsonify() str #返回标准格式的JSON字符串 先序列化JSON的字典,Content-type中加入 				Application/json
 #例子:
    from flask import jsonfy
    ...
    return jsonfy(**dict)
Flask 1.1.1 版本中 可以直接返回字典格式,无需jsonify

# Flask 中的 请求 Request

request.method #获取请求方式 
request.form   #获取FormData中的数据 也就是所谓的Form标签 to_dict()
request.args   #获取URL中的数据 to_dict()

request.json  	#请求中 Content-Type:application/json 请求体中的数据 被序列化到 				request.json 中以字典的形式存放
request.data	#请求中 Content-Type 中不包含 Form 或 FormData 保留请求体中的原始				数据 b""
request.files 	#获取Form中的文件

request.path	#请求路径 路由地址
request.url		#访问请求的完整路径包括 url参数
request.host	#主机位 127.0.0.1:5000 
request.cookies #字典获取浏览器请求时带上的Cookie
request.values  # 所有的请求参数

 #request.values 获取 url 和 FormData 中的数据 敏感地带
      request.args.get('id')
      request.args.getlist('ids')    # 获取多个值

      data = request.args
      data_dict = data.to_dict()     # 通过to_dict() 方法 转化成 字典

      from urllib.parse import urlencode,quote,unquote
      url = urlencode(data_dict)    # 转换成 url
      quote -->> url编码
    
request.headers
request.full_path
request.script_root
request.base_url
request.url_root
request.host_url     
request.remote_addr    # 获取 ip

request.files  # 获取form上传的文件
    obj = request.files['the_file_name']
    obj.save('/var/www/uploads/' + secure_filename(f.filename)) #保存在此,注意我们使用了secure_filename生成一个字符串的名字,以防止文件重名被覆盖

# 定制响应体

  • 使用 make_response 方法给响应体中添加信息,包括响应头和响应体

    1、我们在使用模板渲染的时候使用render_template,如下:
    def index():
        return render_template('index.html', foo=42)
        
    2、当我们想给响应体/响应头中添加附加信息时,我们将使用make_response
    def index():
        response = make_response(render_template('index.html', foo=42))
        response.headers['X-Parachutes'] = 'parachutes are cool'
        return response
    
    3、response是flask.wrappers.Response类型,可以查看源码,还可以设置其他属性
    
        response.set_cookie('key', 'value')
        response.delete_cookie('key')
        response.headers['X-Something'] = 'A value'
    

# Session

​ 除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。

  • **交由客户端保管机制:**在服务端设置了 secret_key 之后,当用户成功登陆后,会生成一个session保存在客户端的浏览器上的cookie中,当下次浏览器携带这个cookie来时,取出session,根据自己的secret_key按位相与最后再序列化成字典即可,也就是说只要我的secret_key 没变响应的就能序列化出对应的字典信息

  • # 基本使用:
    设置:session['username']'xxx'
    删除:session.pop('username', None)
    
    from flask import Flask, session, redirect, url_for, escape, request
    app = Flask(__name__)
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
    
    @app.route('/')
    def index():
        if 'username' in session:
            return 'Logged in as %s' % escape(session['username'])
        return 'You are not logged in'
     
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            session['username'] = request.form['username']
            return redirect(url_for('index'))
        return '''
            <form action="" method="post">
                <p><input type=text name=username>
                <p><input type=submit value=Login>
            </form>
        '''
     
    @app.route('/logout')
    def logout():
        # remove the username from the session if it's there
        session.pop('username', None)
        return redirect(url_for('index'))
    
  • # 自定义session
    pip3  install Flask - Session  #安装Flask-Session
    
       #文件中run.py-------------------------------
    from flask import Flask
    from flask import session
    from pro_flask.utils.session import MySessionInterface
    
    app = Flask(__name__)
    
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
    app.session_interface = MySessionInterface()
    
    
    @app.route('/login.html', methods=['GET', "POST"])
    def login():
        print(session)
        session['user1'] = 'alex'
        session['user2'] = 'alex'
        del session['user2']
    
        return "内容"
    
    
    if __name__ == '__main__':
        app.run()
    
      #utils文件中的session.py------------------------
    import uuid
    import json
    from flask.sessions import SessionInterface
    from flask.sessions import SessionMixin
    from itsdangerous import Signer, BadSignature, want_bytes
    
    
    class MySession(dict, SessionMixin):
        def __init__(self, initial=None, sid=None):
            self.sid = sid
            self.initial = initial
            super(MySession, self).__init__(initial or ())
    
        def __setitem__(self, key, value):
            super(MySession, self).__setitem__(key, value)
    
        def __getitem__(self, item):
            return super(MySession, self).__getitem__(item)
    
        def __delitem__(self, key):
            super(MySession, self).__delitem__(key)
    
    
    class MySessionInterface(SessionInterface):
        session_class = MySession
        container = {}
    
        def __init__(self):
            import redis
            self.redis = redis.Redis()
    
        def _generate_sid(self):
            return str(uuid.uuid4())
    
        def _get_signer(self, app):
            if not app.secret_key:
                return None
            return Signer(app.secret_key, salt='flask-session',
                          key_derivation='hmac')
    
        def open_session(self, app, request):
            """
            程序刚启动时执行,需要返回一个session对象
            """
            sid = request.cookies.get(app.session_cookie_name)
            if not sid:
                sid = self._generate_sid()
                return self.session_class(sid=sid)
    
            signer = self._get_signer(app)
            try:
                sid_as_bytes = signer.unsign(sid)
                sid = sid_as_bytes.decode()
            except BadSignature:
                sid = self._generate_sid()
                return self.session_class(sid=sid)
    
            # session保存在redis中
            # val = self.redis.get(sid)
            # session保存在内存中
            val = self.container.get(sid)
    
            if val is not None:
                try:
                    data = json.loads(val)
                    return self.session_class(data, sid=sid)
                except:
                    return self.session_class(sid=sid)
            return self.session_class(sid=sid)
    
        def save_session(self, app, session, response):
            """
            程序结束前执行,可以保存session中所有的值
            如:
                保存到resit
                写入到用户cookie
            """
            domain = self.get_cookie_domain(app)
            path = self.get_cookie_path(app)
            httponly = self.get_cookie_httponly(app)
            secure = self.get_cookie_secure(app)
            expires = self.get_expiration_time(app, session)
    
            val = json.dumps(dict(session))
    
            # session保存在redis中
            # self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)
            # session保存在内存中
            self.container.setdefault(session.sid, val)
    
            session_id = self._get_signer(app).sign(want_bytes(session.sid))
    
            response.set_cookie(app.session_cookie_name, session_id,
                                expires=expires, httponly=httponly,
                                domain=domain, path=path, secure=secure)
    
  • # 第三方session
    """
    pip3 install redis
    pip3 install flask-session
    """
    
    from flask import Flask, session, redirect
    from flask.ext.session import Session
    
    
    app = Flask(__name__)
    app.debug = True
    app.secret_key = 'asdfasdfasd'
    
    
    app.config['SESSION_TYPE'] = 'redis'
    from redis import Redis
    app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379')
    Session(app)
    
    
    @app.route('/login')
    def login():
        session['username'] = 'alex'
        return redirect('/index')
    
    
    @app.route('/index')
    def index():
        name = session['username']
        return name
    
    
    if __name__ == '__main__':
        app.run()