Flask介绍

Flask是一个基于python依赖jinjia2模板和Werkzeug WSGI 服务的一个微型框架.  werkzeug和Django中的wsgiref模块一样其本质就是一个socket服务端,,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

Werkzeug 并不是 一个框架,它是一个 WSGI 工具集的库,你可以通过它来创建你自己的框架或 Web 应用

Flask 的特点: 短小精悍,易于扩展第三方组件丰富.

Flask是一个微框架,

微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。

默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。

flask和djano的区别?

djano大而全,无socket,借助第三方模块wsgiref模块,内部提供: ORM admin 中间件,Form,ModelForm,session,缓存,信号CSRF
Flask ,短小精悍,易于扩展,无socket,借助第三方库Werkzeug.第三方组件丰富

flask官网https://flask.palletsprojects.com/en/1.1.x/ 1.1版本

Flask的简单应用

#一个基于werkzeug库的Wsgi应用应该是这样的
from werkzeug.wrappers import Request,Response
from werkzeug.serving import run_simple

@Request.application
def hello(request):
    return Response('Hello,werkzeug')

if __name__ == '__main__':
    run_simple('localhost',4000,hello)

 Flask就是基于werkzeug来开发应用程序的

#简单的Flask应用 hello
from flask import Flask#导入Flask类
app=Flask(__name__)#实例化一个对象,这个对象就是我们的W基于WSGI的应用程序,括号里的参数是给对象起的名字,随便一个字符串就可以,__name__表示的是模块名字的字符串
@app.route('/index') #router函数把url和视图函数捆绑起来
def index():
    return 'Hello Flask'

if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)#相当于run_simple('localhost',5000,index),

启动方式

方法一:app.run(),或者在命令行中运行该项目的启动文件python xxx.py

方法二,在命令行中,启动输入flask run 就启动了.Flask通过依赖包Click内置了一个命令行交互系统,当我们按照flask后,会自动添加一个flask命令脚本,我们可以通过执行flask命令来执行该脚本.注意这个启动文件必须是app.py才可以用

 

调式模式

虽然 run() 方法适用于启动本地的开发服务器,但是你每次修改代码后都要手动重启它。这样并不够优雅,注意不能用在生产环境中.原因为:

#在服务上开启调试模式会有很大的安全隐患,攻击装会通过你的调试信息来获取你的数据库结构.另一方面调试页面显示的错误内容会让我们用户很反感

在开发模式下flask会自动激活Wergzurg内置的调试器和重载器.

 

1.是debug=True使用了flask自带的服务器,性能差,

2.这样会把我们的错误信息打印到前端,所以在生产环境中要删除掉debug=True

有两种方法来改变这个弊端

方法一: 直接在应用对象上设置

if __name__ == '__main__':
    app.debug=True
    app.run()#相当于run_simple('localhost',5000,index),

方法二: 在run函数中设置

app.run(debug=True)

配置文件

 flask中的配置文件是一个flask.config对象(继承字典)

 flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
 2     {
 3         'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
 4         'TESTING':                              False,                          是否开启测试模式
 5         'PROPAGATE_EXCEPTIONS':                 None,                          
 6         'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
 7         'SECRET_KEY':                           None,
 8         'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
 9         'USE_X_SENDFILE':                       False,
10         'LOGGER_NAME':                          None,
11         'LOGGER_HANDLER_POLICY':               'always',
12         'SERVER_NAME':                          None,
13         'APPLICATION_ROOT':                     None,
14         'SESSION_COOKIE_NAME':                  'session',
15         'SESSION_COOKIE_DOMAIN':                None,
16         'SESSION_COOKIE_PATH':                  None,
17         'SESSION_COOKIE_HTTPONLY':              True,
18         'SESSION_COOKIE_SECURE':                False,
19         'SESSION_REFRESH_EACH_REQUEST':         True,
20         'MAX_CONTENT_LENGTH':                   None,
21         'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
22         'TRAP_BAD_REQUEST_ERRORS':              False,
23         'TRAP_HTTP_EXCEPTIONS':                 False,
24         'EXPLAIN_TEMPLATE_LOADING':             False,
25         'PREFERRED_URL_SCHEME':                 'http',
26         'JSON_AS_ASCII':                        True,
27         'JSON_SORT_KEYS':                       True,
28         'JSONIFY_PRETTYPRINT_REGULAR':          True,
29         'JSONIFY_MIMETYPE':                     'application/json',
30         'TEMPLATES_AUTO_RELOAD':                None,
31     }
View Code

如果我们要更改配置文件的话用这种方法

settings.py

class Config(object):
    DEBUG = False
    TESTING = False
    DATABASE_URI = 'sqlite://:memory:'

class ProductionConfig(Config):
    """
    生产环境
    """
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    """
    开发环境
    """
    DEBUG = True

class TestingConfig(Config):
    """
    测试环境
    """
    TESTING = True

#PS: 从sys.path中已经存在路径开始写


#PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
seetings.py

导入配置文件使用app.config.from_object(),注意这里使用的是模块的导入而不是路径的导入

flask_demo.py

from flask import Flask
app=Flask(__name__)
app.config.from_object('seetings.DevelopmentConfig') #直接导入配置文件中的类就可以了
@app.route('/index')
def index():
    return 'Hello Flask'

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

flask_demo

使用配置文件中的数据

我们知道flask中的app.comfig是一个字典对象,注意配置文件中的变量名要大写,小写不加载。源码中规定的

所以配置文件有这么几种情况

情况一:配置文件为这样

#config.py
DEBUG = TRUE
from flask import Flask

app = Flask(__name__)
app.config.from_object('config')  # 这样导入


def hello():
    return 'hello'


app.add_url_rule('/hello', view_func=hello)

if __name__ == '__main__':
    app.run(debug=app.config['DEBUG'])

情况二:配置文件为这样

# config.py
class Development(object):
    DEBUG = True


CONFIG_DICT = {
    'development': Development
}
from flask import Flask

app = Flask(__name__)
app.config.from_object('config')  # 这样导入


def hello():
    return 'hello'


app.add_url_rule('/hello', view_func=hello)

if __name__ == '__main__':
    app.run(debug=app.config["CONFIG_DICT"]["development"].DEBUG)

还可以这样导入,这个是从同事那里偷学来的。这种形式我感觉比较好

from flask import Flask
from config import CONFIG_DICT

app = Flask(__name__)
app.config.from_object(CONFIG_DICT["development"])  # 这样导入就可以直接用你类中定义的变量了


def hello():
    return 'hello'


app.add_url_rule('/hello', view_func=hello)

if __name__ == '__main__':
    # print(app.config["DEBUG"])
    app.run(debug=app.config["DEBUG"])

 

路由

route() 装饰器把一个函数绑定到对应的 URL 上。

静态路由

添加路由的两种方式:

# 路由方式一(*):装饰器
            @app.route('/index',methods=['GET','POST']) #当有post提交时
            def index():
                return "Index"
#路由方式二 路由方式一种的装饰器也是用的这种方法
    def order():
    return 'Order'
app.add_url_rule('/order',view_func=order)#view_func绑定那个函数

动态路由

我们不仅可以为视图函数绑定多个url,还可以在URL规则中添加变量部分,使用"<变量名>"的形式表示.Flask会把变量传入到视图函数中.

格式为: <转换器的类型:参数名称>  转换器(其实就是要求参数的格式类型)。

from flask import Flask
app=Flask(__name__)
@app.route('/index/<int:id>/') #参数id要求为int类型
def index(id):#别忘记传参
    return 'Hello Flask%s'%(id)

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

当url规则中包含变量时,如果用户访问的URL没有添加变量,比如/index,那么服务器会报一个404错误,通常情况下我们会在app.router装饰器中使用defaults参数设置url变量的默认值,这个参数作为字典来输入.

from flask import Flask
app=Flask(__name__)

@app.route("/index,defaults={'id':1}
@app.route('/index/<int:id>/') 
def index(id):
    return 'Hello Flask%s'%(id)

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

  

转换器有下面几种:

@app.route('/user/<username>')   #常用的   不加参数的时候默认是字符串形式的
@app.route('/post/<int:post_id>')  #常用的   #指定int,说明是整型的
@app.route('/post/<float:post_id>') #浮点
                                                                       @app.route(
'/post/<path:path>') #路径,可以拼接路径 @app.route('/login', methods=['GET', 'POST'])

常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

 

router中的url一个/和两个/的区别?

唯一 URL / 重定向行为
Flask 的 URL 规则基于 Werkzeug 的路由模块。这个模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且唯一的 URL。

以这两个规则为例:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'
虽然它们看起来着实相似,但它们结尾斜线的使用在 URL 定义 中不同。 第一种情况中,指向 projects 的规范 URL 尾端有一个斜线。这种感觉很像在文件系统中的文件夹。
访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范 URL 去。 然而,第二种情况的 URL 结尾不带斜线,类似 UNIX
-like 系统下的文件的路径名。访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。 这个行为使得在遗忘尾斜线时,允许关联的 URL 接任工作,与 Apache 和其它的服务器的行为并无二异。此外,也保证了 URL 的唯一,有助于避免搜索引擎索引同一个页面两次。

 构造url(反向解析)

 web程序中的URL无处不在,如果程序中的url都是以硬编码的方式(即完整的url)写出,那么将会大大的降低代码的易用性,比如你修改了某一个路由规则,那么在视图中对应的url都要修改一遍,为解决这个问题,url_for函数就出现了,你在视图中直接用url_for(函数名),就可以获取真正的URL了.注意这里获得是相对路径只能在程序内部使用.

注意:endpoint不写,别名默认视图函数名字

from flask import Flask,url_for#反向解析需要导入url_for 相当于Django中的reverse
app=Flask(__name__)
@app.route('/aa/',endpoint='index') #endpoint相当于django中的name,主要是起别名的作用
def index():
    v=url_for('login')#反向解析
    print(v)#打印
    return '这是index页面'

@app.route('/login/',endpoint='login')
def login():
    return '这是登录页面'


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

结果:

/login/

自定义正则转换器

当工作中遇到url中传递正则表达式的情况,我们可以自定义正则表达式转换器

首先我们要继承BaseConverter这个正则类,我们来看一下源码

class BaseConverter(object):

    """Base class for all converters."""
    regex = '[^/]+'  #这里就是正则表达式,如果我们不写就用它的
    weight = 100  #这里没看懂

    def __init__(self, map):  
        self.map = map

    def to_python(self, value): #这里对用正则匹配到的数据是否进行修饰,默认没有修饰
        return value

    def to_url(self, value): #这个方法就是我们用url_for方法进行反向生成的时候时,所调用的方法
        return url_quote(value, charset=self.map.charset)

 

我们现在的项目需求是,用户传过来的参数,uid前缀返回给视图函数

class RegexConverter(BaseConverter):
    def __init__(self, url_map, regex):
        super(RegexConverter, self).__init__(url_map)  # 继承BaseConverter中的init参数
        self.regex = regex  # 输入我们自己的正则表达式,用来替代基类中的regex

    def to_python(self, value):
        return "uid" + value

    def to_url(self, value):
        val = super(RegexConverter, self).to_url(value)
        return val


app.url_map.converters['regs'] = RegexConverter #在app.route装饰器中我们定义正则时前缀要使用regs,否则它会匹配不到


@app.route('/index/<regs("\d+"):nid>', methods=["GET"])
def index(nid):
    print("nid的结果:", nid)
    return "这是index页面"

我们这样在前端输入

 

 后端的结果为:

nid的结果为: uid22

注意这里有一个坑:我们定义了正则前端输入的参数就必须是我们正则中要求的,否则会报错,不能输入127.0.0.1:3000/index/22aa  这样是匹配不出来的

 

这是另外的一个例子

from flask import Flask ,render_template,redirect,url_for
from werkzeug.routing import BaseConverter #自定义正则表达式就要继承BaseConverter这个类

app=Flask(__name__)
class RegexConverter(BaseConverter):
    '''
    自定义url匹配正则表达式转换器
    '''
    def __init__(self,map,regex):
        super(RegexConverter,self).__init__(map)
        self.regex=regex
    def to_python(self, value):
        '''
        路由匹配时,匹配成功后传递给视图函数中的参数值
        :param value:
        :return:
        '''
        return int(value)
    def to_url(self, value):
        '''
        使用url_for反向生成url时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        :param value:
        :return:
        '''
        val=super(RegexConverter, self).to_url(value)
        return val

#先要把你写的正则转换器添加到Flask的转换器中 app.url_map.converters['regs']=RegexConverter
@app.route(
'/index/<regs("\d+"):nid>',methods=['GET','POST']) #使用自定义的转换器 def index(nid): print(nid,type(nid)) v=url_for("index",nid=555)#相当于/index/555 print(v) return "这是Index页面" if __name__ == '__main__':

重定向

当需要重定向时.可以用rediret_to参数

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

                @app.route('/index',methods=['GET','POST'],redirect_to='/new')
                def index():
                    return "老功能"

                @app.route('/new',methods=['GET','POST'])
                def new():
                    return '新功能'


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

 获取子域名

首先我们要知道:域名是由子域名和顶级域名构成的.

一级域名:又叫顶级域名,一串字符串中间一个点隔开,例如baidu.com,这里说明一下,www.baidu.com不是一级域名!!而是二级域名!

二级域名:实际上就是一个一级域名以下的主机名,一串字符串中间两个“.”隔开,例如pan.baidu.com("pan"就是主机名)。

三级域名:二级域名的子域名,特征是包含三个“.”,一般来说三级域名都是免费的。

#这是顶级域名
https://baidu.com/
#这是顶级域名的子域名
https://www.baidu.com/
https://pan.baidu.com
from flask import Flask,url_for#反向解析需要导入url_for 相当于Django中的reverse
app=Flask(__name__)
@app.route('/aa/',subdomain="admin")
def index():
    v=url_for('login')#反向解析
    print(v)
    return '这是index页面'

@app.router中的参数

 

@app.route和app.add_url_rule参数:
            rule,                       URL规则
            view_func,                  视图函数名称
            defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
            endpoint=None,              名称,用于反向生成URL,即: url_for('名称')
            methods=None,               允许的请求方式,如:["GET","POST"]
            

            strict_slashes=None,        对URL最后的 / 符号是否严格要求,
                                        如:
                                            @app.route('/index',strict_slashes=False),
                                                访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
                                            @app.route('/index',strict_slashes=True)
                                                仅访问 http://www.xx.com/index 
            redirect_to=None,           重定向到指定地址
                                        如:
                                            @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
                                            或
                                            def func(adapter, nid):
                                                return "/home/888"
                                            @app.route('/index/<int:nid>', redirect_to=func)

            subdomain=None,             子域名访问
                                                from flask import Flask, views, url_for

                                                app = Flask(import_name=__name__)
                                                app.config['SERVER_NAME'] = 'haiyan.com:5000'


                                                @app.route("/", subdomain="admin")
                                                def static_index():
                                                    """Flask supports static subdomains
                                                    This is available at static.your-domain.tld"""
                                                    return "admin.xxx.com"

                            #动态生成
                                                @app.route("/dynamic", subdomain="<username>")
                                                def username_index(username):
                                                    """Dynamic subdomains are also supported
                                                    Try going to user1.your-domain.tld/dynamic"""
                                                    return username + ".your-domain.tld"


                                                if __name__ == '__main__':
                                                    app.run()
@app.route和app.add_url_rule参数

Flask中的FBV

其实Flask我们用的最多的就是FBV,CBV用的很少,

这里简单介绍下CBV

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

app = Flask(__name__)


def cal_time(func):
    """
    计算请求所用的时间
    :param func:
    :return:
    """
    wraps(func)

    def inner(*args, **kwargs):
        start_time = time.time()
        result = func()
        end_time = time.time()
        print(f'该函数执行了{end_time - start_time}s秒')
        return result

    return inner


class Login(views.MethodView):
    methods = ['POST', 'GET']
    # 如果需要在CBV中加装饰器的话,括号里就是装饰器的名字,可以传多个,当有一个的时候别忘记了加逗号(,)
    decorators = (cal_time,)

    def get(self):
        time.sleep(1)
        print('get 请求')
        return 'login get'

    def post(self):
        time.sleep(2)
        print('post 请求')
        return 'login post'


app.add_url_rule('/login', view_func=Login.as_view(name='login'))  # name=endpoint

if __name__ == "__main__":
    app.run(debug=True, host='127.0.0.1', port=5001)

 

视图中添加装饰器

当我们在Flask中的视图中添加装饰器时.我们要添加的装饰器和路由装饰器会出现冲突,这时候应该办

首先我们要确定的路由装饰器一定要先放到我们的装饰器的上边,因为它要先执行,才能再执行我们的装饰器,

当我们有多个视图函数需要装饰时候,比如这样

from flask import Flask#导入Flask类
app=Flask(__name__)
def wrapper(f):
    def inner(*args,**kwargs):
        print('before')
        return f()
    return inner
@app.route('/index1') 
@wrapper
def index():
    return 'Hello Flask'

@app.route('/login') 
@wrapper
def login():
    return 'Hello Flask'
if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)

 

这时候就会出现错误

ssertionError: View function mapping is overwriting an existing endpoint function: inner

因为 endpoint默认反向生成函数名,然而,被装饰的函数的真正的函数名都是inner,所以endpoint这时候就出错了,这时候我们就需要真正的函数名,于是就用到了wraps装饰器修复

from flask import Flask
from functools import wraps  #导入wraps
app=Flask(__name__)
def wrapper(f):
    @wraps(f)
    def inner(*args,**kwargs):
        print('before')
        return f()
    return inner
@app.route('/index1')
@wrapper
def index():
    return 'Hello Flask'

@app.route('/login')
@wrapper
def login():
    return 'Hello Flask'
if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)
加wraps后

HTTP方法

HTTP有许多访问url的方法,在默认情况下,只支持GET方法,但是,可以定义router中的参数来实现多种HTTP方法

比如当时post请求的时候

@app.route('/login',methods=['GET','POST']) #这里的括号里就要有method参数了

请求对象(request)和响应对象

Request

首先我们应该知道,djano中的请求数据是通过参数request传递给视图函数的,但是在Flask不是通过参数传递进来的,而是通过上下文管理

首先从 flask 模块里导入它:

from flask import request

当前请求的 HTTP 方法可通过 method 属性来访问。通过:attr:~flask.request.form 属性来访问表单数据( POST 或 PUT 请求提交的数据)。

当访问form表单数据(POST提交)时

request.form.get()

注意:不存在时,得到的值为None.

当访问url参数(get提交时)

request.args.get()

注意:不存在时,得到的值为None.

request的相关方法

 

请求相关信息
        # request.method    请求方法
        # request.args      当get请求,这里得到的是不可变的字典,也就是不可改变该字典里的数据,如果要变为可变字典,可以用to_dict()
        # request.form      当post请求时
        # request.get_json() #这里有个小坑,默认情况下,当contentType是json时才能解析出数据,还可以通过设置参数force=True 忽视contentType=application/json的限制
        # request.values   
        # request.cookies
        # request.headers
        # request.path
        # request.full_path
        # request.script_root
        # request.url
        # request.base_url
        # request.url_root
        # request.host_url
        # request.host
        # request.files
     # request.remote_add 获取用户ip
# obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename))

重点来介绍一下

1.request.values() 

此方法可以获取get请求和post请求的所有的参数

print(request.values)  
# CombinedMultiDict([ImmutableMultiDict([('id', '1'), ('age', '20')]), ImmutableMultiDict([('user', 'Oldboy'), ('pwd', 'DragonFire')])])
print(request.values.get("id"))  # 1
print(request.values["user"])  # Oldboy
# 这回喜欢直接操作字典的小伙伴们有惊喜了! to_dict() 方法可以直接将我们的参数全部转为字典形式
print(request.values.to_dict()) # {'user': 'Oldboy', 'pwd': 'DragonFire', 'id': '1', 'age': '20'}

# 注意这里的坑来啦! 坑来啦!
# 如果url和form中的Key重名的话,form中的同名的key中value会被url中的value覆盖
# http://127.0.0.1:5000/req?id=1&user=20
print(request.values.to_dict())  # {'user': 20 'pwd': 'DragonFire', 'id': '1'}

 

 

 response

所有返回给前端的数据都是Response这个对象或子类,包括return "2"

在flask中返回数据给前端有三种方式

一:直接使用 return   

在这里有三种情况,

情况一:如果返回的value直接是字符串,这个value直接被转换成一个包含(value,status=200,text/html mimetype)的对象

@app.route("/index/2")
def index2():
    country = "中国"
    return country

页面信息为:

 

 

 

 

 

 情况二如果返回的是一个字典,则会调用jsonify方法生成一个response(Content-Type:application/json)

 

 

 

 

 

 情况三:

如果返回元组,元组中的项可以提供额外的信息。这些元组必须采用(response,status),(response,headers)或(response,status,headers)的形式。状态值将覆盖状态代码,标题可以是附加标题值的列表或字典。

二:返回使用make_response生成一个Response对象

如果你想要得到响应对象并对其进行修改

def index():
    response = make_response(render_template('index.html', foo=42))
    response.headers['X-Parachutes'] = 'parachutes are cool'
    return response

三:返回Response对象

你可以直接返回一个response对象,但是源码不建议你这样用,如果你想直接返回一个Response对象时,请使用make_response方法

 

 

 

#源码
The response object that is used by default in Flask.  Works like the
    response object from Werkzeug but is set to have an HTML mimetype by
    default.  Quite often you don't have to create this object yourself because
    :meth:`~flask.Flask.make_response` will take care of that for you.

 

不过从注释中,我们得到两条很有用的信息:

  1. 一般情况下不要直接操作 Response 对象,而是使用 make_response 方法来生成它
  2. 如果需要使用自定义的响应对象,可以覆盖 flask app 对象的 response_class 属性。

 

 

 

 # 响应相关信息
        return "字符串"   #返回字符串
        return render_template('html模板路径',**{})  #渲染模板
        return redirect('/index.html')     #重定向
        return json.dumps() #返回json数据,和他一样的就是jsonify()
    

设置响应头和cookie

  #####如何设置响应头和cookie*******
        response = make_response(render_template('index.html') ) #生成对象
        #response是flask.wrappers.Response类型
        response.set_cookie('key', 'value')  #设置cookie
        response.delete_cookie('key')   #删除cookie
         
        response.headers['X-Something'] = 'A value'  #设置响应头
        return response   #返回respon对象

补充

from urllib.parse import urlencode,quote,unquote

val = "%E6%8A%8A%E5%87%A0%E4%B8%AA"
print(unquote(val))   #ba上面这样的数据转换成中文
vall='我是中国人'
print(quote(vall))
vdd={"name":'小红'}
print(urlencode(vdd))

模板引擎

大多数和djano的模板渲染差不多

首先要建立一个名为templates的文件在根目录下,文件夹中放html

from flask import Flask,render_template
app=Flask(__name__)
@app.route('/xxx') 
def xxx():
    return render_template('/index.html',msg='fdsfd')

if __name__ == '__main__':
    print(type(__name__))
    app.run(debug=True)#相当于run_simple('localhost',5000,index),

1、为了防止xss攻击,加了验证,所以页面上显示字符串的形式,解决办法,有两种方式

  - 方法一在后端Markup

 v5 = Markup("<input type='text' />")

  -方法二 在前端

  {{ v4|safe }}

2、自定义方法

复制代码
def test(a,b):
    return a+b

@app.route('/index')
def index():
    return render_template("index2.html",test=test)


index2.html
<h1>{{ test(1,2) }}</h1>
复制代码

3、写一个函数在所有的页面都使用

template_global和template_filter

复制代码
@app.template_global()
def sb(a1, a2):
    return a1 + a2


@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3
复制代码
调用方式:{{sb(1,2)}}  {{ 1|db(2,3)}}

4、模板继承:和django的一样。extents

5.后端传函数,前端使用模板语言的时候需要加括号然而djano不需要

def sb(a1, a2):
    content={
        'k1':lambda x:5
    }

    return render_template('index.html',**content)
#前端
{{k1()}}

 

模板其他问题参考django 

session

它允许你在不同请求间存储特定用户的信息。

Flask中的session和djano中的session是不同的,Flask中的session放到cookie中,它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名。这意味着用户可以查看你 Cookie 的内容,但却不能修改它,除非用户知道签名的密钥。

一个完整的范例

要求 登陆后把信息添加session中

flask.py

from flask import Flask,render_template,request,redirect,session
app=Flask(__name__)
app.secret_key='wefjldsaj' #先要进行密钥签名
USER_DICT={
    '1':{'name':'小明'},
    '2':{'name':'小红'},
    '3':{'name':'36'}
}#模拟一个数据库
@app.route('/login',methods=['GET','POST'])
def login():
    print('请求来了')
    if request.method=="POST":
        username=request.form.get('username')#从form表单中取数据
        password=request.form.get('password')
        #request.args #对应get从url中取数据
        if username=='alex' and password=="123":
            #登录成功后把用户信息放入session中
            session['user_info']=username
            return redirect('/index')
        else:
            return render_template('/login.html',msg="用户名和密码错误") #还可以这样传值return render_template('/login.html',**{'msg':"用户名和密码错误"})
    return render_template('/login.html')
@app.route('/index')
def index():
    """
    主页
    :return:
    """
    username=session.get('user_info')
    if not username:
        return redirect('/login')
    print(USER_DICT)
    return render_template('/index.html',user_dict=USER_DICT)
@app.route('/logout')
def logout():
    session.pop('user_info',None)
    return '已经退出'
if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  <!--IE浏览器最高渲染-->
    <meta name="viewport" content="width=device-width, initial-scale=1"> <!--为了确保适当的绘制和缩放-->
    <title>主页</title>
    <link rel="stylesheet" href="../bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>
<ul>
    {% for k,v in user_dict.items() %}
    <li>{{v.name}}</li>
    {% endfor%}
</ul>
<script src="../jquery-3.2.1.min.js"></script>

</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  <!--IE浏览器最高渲染-->
    <meta name="viewport" content="width=device-width, initial-scale=1"> <!--为了确保适当的绘制和缩放-->
    <title>用户登录</title>
    <link rel="stylesheet" href="../bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>
<h1>请登录</h1>
<form method="post">
    用户名:<input type="text" name="username">
    密码: <input type="password" name="password">{{msg}}
    <input type="submit" value="提交">
</form>
<script src="../jquery-3.2.1.min.js"></script>

</body>
</html>

 

 

from flask import Flask,url_for,session

app = Flask(__name__)
app.secret_key = "sdsfdgdgdgd"
app.config['SESSION_COOKIE_NAME'] = 'session_lvning'  #设置session的名字

@app.route('/index/')
def index(nid):
    #session本质上操作的是字典, session是否还有其他方法?与字典方法相同
    #session的原理:如果下一次访问的时候带着随机字符串,会把session里面对应的
    # 值拿到内存,假设session保存在数据库,每执行一次链接一次数据库,每次都要时时更新的话
    # 会非常损耗内存
    session["xxx"] = 123
    session["xxx2"] = 123
    session["xxx3"] = 123
    session["xxx4"] = 123
    del session["xxx2"]  #在这删除了,真正存储的时候是没有xxx2的
    return "ddsf"

if __name__ == '__main__':

 

消息闪现

一般一个用户操作完成后,我们要告诉用户操作的结果,用户看完后这个消息就没必要存在了,我们就可以删除它.

Flask的消息闪现系统就是提供了这个功能.也就是反馈

flask()它可以将要闪现的内容存储起来.

get_flashed_messages():它可以把储存的内容取出来显示,然后自动删掉.

本质:闪现是基于session创建的,flash支持往里边放值,只要你取一下就没有了,相当于pop了一下。不仅把值取走,而且吧session里的东西去掉,可以用session直接写

from flask import Flask,flash,get_flashed_messages
app = Flask(__name__)
app.secret_key = 'asdfasdfasdf'
@app.route('/x1',methods=['GET','POST'])
def login():
    flash('我要上向龙',category='x1') #把内容放入flash中,category就是一个别名 
    return '视图函数x1'
@app.route('/x2',methods=['GET','POST'])
def index():
    data = get_flashed_messages(category_filter=['x1']) #取数据
    print(data)
    return "视图函数x2"

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

先执行一次/x1

执行两次/X2

结果

['我要上向龙']
[] #第二次就取不出值来了

用的场景: 阅后即焚

登录场景:

flash.py

rom flask import render_template, request, session, url_for, redirect, flash
 
@app.route('/')
def index():
    if 'user' in session:
        return render_template('hello.html', name=session['user'])
    else:
        return redirect(url_for('login'), 302)
 
@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        session['user'] = request.form['user']
        flash('Login successfully!')
        return redirect(url_for('index'))
    else:
        return '''
        <form name="login" action="/login" method="post">
            Username: <input type="text" name="user" />
        </form>

login.html

!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class="flash">
    {% for message in messages %}
      <li>{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}
<div class="page">
    {% block body %}
    {% endblock %}
</div>

 全局装饰器

@app.before_request和@app.after_request

app.before_request和app.after_request相当于djano中的中间件函数process_request和process_response,服务于全局,

不同点:当before_request有返回值时.它会直接执行所有的after_request,而不是像djano中执行对应的after_request函数

#after_request
如果没有抛出错误,在每次请求后执行    
接受一个参数:视图函数作出的响应
在此函数中可以对响应值在返回之前做最后一步修改处理
需要将参数中的响应在此参数中进行返回

@app.after_request
    def after_request(response):
        return response
————————————————
版权声明:本文为CSDN博主「csdn-gdj」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guodejie/article/details/80795770

 

项目中遇到的知识点: 在项目中我们用after_request全局装饰器,装饰了一个跨域函数

from flask import Flask,session

app=Flask(__name__)
app.secret_key="dfsadsfds"
@app.route('/login')
def login():
    session['login_info']=123
    return '123'

@app.route('/index3')
def index():
    ret=session.pop('login_info')
    print('ret',ret)
    return '这是主页'

@app.before_request
def before():
    print('brefore')

@app.after_request
def after(response): #注意这里的参数和返回值
    print('after')
    return response

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

结果:

brefore
after

还有其他的一些全局装饰器

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, Request, render_template

app = Flask(__name__, template_folder='templates')
app.debug = True


@app.before_first_request
def before_first_request1():
    print('before_first_request1')


@app.before_first_request
def before_first_request2():
    print('before_first_request2')


@app.before_request
def before_request1():
    Request.nnn = 123
    print('before_request1')


@app.before_request
def before_request2():
    print('before_request2')


@app.after_request
def after_request1(response):
    print('before_request1', response)
    return response


@app.after_request
def after_request2(response):
    print('before_request2', response)
    return response


@app.errorhandler(404)
def page_not_found(error):
    return 'This page does not exist', 404


@app.template_global()
def sb(a1, a2):
    return a1 + a2


@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3


@app.route('/')
def hello_world():
    return render_template('hello.html')


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

中间件

这个中间件是发生在request形成之前,也就是在执行app.wsgi_app()之前添加一些操作,通过对象的__call__方法

没多大用处,一般我们用@app.before_request

from flask import Flask
from flask import session, request
app = Flask(__name__)
app.secret_key = 'asdfasdfasdf'

@app.route('/x2',methods=['GET','POST'])
def index():
    print('我是index')
    return "我是x2"


class Middleware(object):
    def __init__(self,old_wsgi_app):
        """
        服务端启动时,自动执行
        :param old_wsgi_app:
        """
        self.old_wsgi_app =old_wsgi_app

    def __call__(self, environ, start_response):
        """
        每次有用户请求道来时
        :param args:
        :param kwargs:
        :return:
        """
        print('before')#添加在形成request之前的操作

        obj = self.old_wsgi_app(environ, start_response)
        print('after') #添加在形成request之后的操作
        return obj

if __name__ == '__main__':
    app.wsgi_app = Middleware(app.wsgi_app)
    app.run()
    """
    执行app.run()时候
    1.先执行app.__call__,
    2.再调用app.wsgi_app(),因为我自己有wsgi_app属性,加括号执行我的__call__方法,然后就把老的app.wsgi_app传进来,这样我们就可以在老的app.wsgi_app执行前,和执行后做一些操作
"""
middleware

结果:

before
我是index
after

 蓝图blueprint

详情见flask组件中的数据库连接池

 

蓝图是什么? 简单来说蓝图是一个存储视图方法/模型代码的目录,这些操作在蓝图被注册到flask的APP实例对象应用后就可以被调用了,flask可以通过蓝图来组织URL以及处理客户端请求的视图

特点:

  1. 一个项目可以具有多个蓝图。

  2. 每个蓝图可以有自己的路由前缀

  3. 蓝图目录可以保存单独属于自己的模版目录、静态文件和视图

  4.一个蓝图可以有多个不同的url

第一个功能:如果代码很多要进行归类,blueprint就是对flask的目录结构进行划分(应用于小中型程序),使目录结构更清晰。

#这个怎么理解呢?
比如我们有两个页面一个是金融管理,一个会员管理,一个是美食管理。
每个页面都有好多的函数,这些函数如果放在一起的话,会有很多代码,显得特别的乱。如果我们能分类这些代码,把不同的代码放到不同py文件中就好了。blueprint就是为了解决这个问题的。
在每个py中实例化一个蓝图对象,一共生成三个蓝图对象就可以了。然后再把这些对象注册到app中,这样就方便管理了。

第二个功能:当一个py文件中有多个函数时,为了使url容易区分可以给url添加前缀.

第三个功能: 我们知道@app.before_rerequest对全局生效,然而蓝图可以让它只对某一个蓝图实例生效

我们为什么要用蓝图?

我们的应用经常会区分用户站点和管理员后台
两者虽然都在同一个应用中,但是风格迥异。把它们分成两个应用吧,总有些代码我们想重用;放在一起嘛,耦合度太高,代码不便于管理。所以Flask提供了蓝图(Blueprint)功能。
蓝图使用起来就像应用当中的子应用一样,可以有自己的模板,静态目录,有自己的视图函数和URL规则,蓝图之间互相不影响。但是它们又属于应用中,可以共享应用的配置。
对于大型应用来说,
我们可以通过添加蓝图来扩展应用功能,而不至于影响原来的程序。不过有一点要注意,目前Flask蓝图的注册是静态的,不支持可插拔。 转载于http:
//www.bjhee.com/flask-ad6.html

例子

首先这样创建目录

首先,manage.py文件是我们的启动文件

from blue_flask import app

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

admin.py文件中创建一个蓝图实例

from flask import Blueprint

ad=Blueprint('ad',__name__,url_prefix='/admin') #实例化一个蓝图对象,给url添加前缀

@ad.route('/ad1')
def ad1():
    return '这是ad1页面'
@ad.route('/ad2')
def ad2():
    return '这是ad2页面'

acount.py文件中我们再创建一个蓝图实例,并添加一个before_request装饰器

from flask import Blueprint

act=Blueprint('act',__name__) #实例化一个蓝图对象,里边的名字随便起,但是不要和url相同

@act.route('/acount')
def login():
    return '这是acount'
@act.before_request
def f ():
    print('我只对act蓝图实例有效')

__init__.py文件是我们的初始化文件,在这里我们导入蓝图,并注册蓝图

from flask import Flask
#创建一个app对象
app=Flask(__name__)

from blue_flask.views.admin import ad
from blue_flask.views.acount import act

#给新蓝图注册
app.register_blueprint(ad)
app.register_blueprint(act)

这样我们就可可以执行manage.py文件了,

在url中输入/acount,我们可以在前端看到"这是account",在后台打印出"我只对act蓝图实例有效"

当我们在url这样输入/admin/ad1,在前端会显示这是ad1页面,在后台不会打印出f装饰器的内容

posted on 2018-04-23 21:09  程序员一学徒  阅读(774)  评论(0编辑  收藏  举报