Basic introduction and use

01. Basic introduction and use

Article Directory

1. Introduction

1.web framework

  • It can be called by the server to form a program to return data according to the execution of different logic processing requested by the client
  • **Core: **Realize routing and view (business logic processing)

2. The weight of the frame

  • Heavyweight framework: To facilitate business development, it provides a wealth of tools and components such as Django.
  • Lightweight framework: Only provide the core functions of the web framework, free, flexible, and highly customized. Such as flask, Tornado.

Three mainstream frameworks:

  • django: large and complete, heavy weapons, internally provided: ORM, Admin, ModelForm, Session, cache, model, CSRF.
  • Flask: short and powerful, extensible, and rich in third-party components
  • tornado: short and powerful, asynchronous and non-blocking
other:
  • web.py
  • bottle.py

3. Introduction to Flask

Flask was born in 2010 and is a lightweight web development framework written by Armin ronacher (person's name) in Python language based on the Werkzeug toolbox.

Flask itself is equivalent to a kernel, and almost all other functions need to be extended (mail extension Flask-Mail, user authentication Flask-Login, database Flask-SQLAlchemy), and all need to be implemented with third-party extensions. For example, you can use the Flask extension to add ORM, form verification tools, file upload, identity verification, etc. Flask does not have a default database. You can choose MySQL or NoSQL.

Its WSGI toolbox uses Werkzeug (routing module), and its template engine uses Jinja2. These two are also the core of the Flask framework.

Official official document (Chinese)

4. Compared with django

django provides:

  • django-admin quickly create project engineering
  • manage.py manage project engineering
  • ORM model (database abstraction layer)
  • admin background management site
  • Caching mechanism
  • File storage system
  • User authentication system

And these, flask does not have, all need to be provided by extension packages.

3. Flask commonly used third-party extension packages:

Flask-SQLalchemy: Operating database, ORM. Known as the fastest framework SQLalchemy for operating database;

Flask-script: terminal script tool, scaffolding;

Flask-migrate: Manage the migration database. It is more powerful than Django, and it can be rolled back during the process of data migration;

Flask-Session: Session storage mode specification;

Flask-WTF: form;

Flask-Mail: mail;

Flask-Bable: provide internationalization and localization support, translation;
Flask-Login: authenticate user status;

Flask-OpenID: authentication, OAuth;

Flask-RESTful: a tool for developing REST API;

Flask JSON-RPC: Develop rpc remote service [procedure] call

Flask-Bootstrap: Integrated front-end Twitter Bootstrap framework

Flask-Moment: localized date and time

Flask-Admin: a simple and extensible management interface framework

You can view more extensions officially recommended by Flask through https://pypi.org/search/?c=Framework+%3A%3A+Flask

2. Basic use

1. Installation

pip install flask

2. Reserve knowledge werkzeug

Rely on wsgi werkzeug

Introduction: Werkzeug is a WSGI toolkit, which can be used as the underlying library of a web framework.

Supplement: werkzeug is not a web server, nor a web framework, but a toolkit. The official introduction says that it is a WSGI toolkit. It can be used as the underlying library of a web framework because it encapsulates a lot of web framework things. , Such as Request, Response, etc.

Example 1:

from werkzeug.wrappers import Request, Response

@Request.application
def hello(request):
    return Response('Hello World!')

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, hello)

Example 2:

from werkzeug.wrappers import Request, Response

class Flask(object):
    def __call__(self.serving,run_simple)
    	return '请求来了'
if __name__== 'main':
    run_simple('localhost', 4000, hello)

Example three:

from werkzeug.wrappers import Request, Response

class Flask(object):
    def __call__(self.serving,self):
    	return '请求来了'
    def run(self):
        run_simple('127.0.0.1',5000,self)
app =Flask()
if __name__== 'main':
    app.run()

3. Pycharm creates flask project

Unlike django, flask does not provide any automatic operation, so you need to manually create the project directory, and you need to manually create the management file for the startup project

For example, create the project directory flaskdemo, create manage.py in the directory. Open the project in pycharm and specify the virtual environment created above

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-lQLYKdnn-1622815079222)(http://cdn.taoxiaoxin.club/shawn/20210603214221.png)]

4. Create the flask framework main program

The name can be changed at will, it can beapp.py/run.py/main.py/index.py

from flask import Flask

# 创建flask的应用对象
# __name__表示当前模块的名字
#           模块名,Flask这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录
app = Flask(__name__)


# @app.route('/')
def hello_world():
    '''定义的视图函数'''
    return 'Hello World!'

if __name__ == '__main__':
    # 注意:flask默认端口5000
    app.run()

5. Code analysis

# 导入Flask类
from flask import Flask

"""
import_name      Flask程序所在的包(模块),传 __name__ 就可以
                 其可以决定 Flask 在访问静态文件时查找的路径
static_path      静态文件访问路径(不推荐使用,使用 static_url_path 代替)
static_url_path  静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder    静态文件存储的文件夹,可以不传,默认为 static
template_folder  模板文件存储的文件夹,可以不传,默认为 templates
"""
app = Flask(import_name=__name__)


# 编写路由视图
# flask的路由是通过给视图添加装饰器的方式进行编写的。当然也可以分离到另一个文件中。
# flask的视图函数,flask中默认允许通过return返回html格式数据给客户端。
@app.route(rule='/')
def index():
    return '<mark>Hello Word!</make>'

# 加载项目配置
class Config(object):
    # 开启调试模式
    DEBUG = True

# flask中支持多种配置方式,通过app.config来进行加载,我们会这里常用的是配置类
app.config.from_object( Config )


# 指定服务器IP和端口
if __name__ == '__main__':
    # 运行flask
    app.run(host="0.0.0.0", port=5000)

summary:

  • werkzeugThe WSGI implementation based on the Flask framework , Flask itself does not have WSGI
  • Once the user requests, it will execute the __call__method
  • Standard process for writing Flask
  1. Create a Flask object
  2. Put routing and view together, you can write multiple
  3. The essence of calling app.run() is internally calledwerkzeug

6. Case: Login and display user information

main.py

from flask import Flask,render_template,request,redirect,session,url_for
app = Flask(__name__)
app.debug = True
app.secret_key = 'sdfsdfsdfsdf'

USERS = {
    1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
    2:{'name':'李四','age':28,'gender':'男','text':"搞钱第一条"},
    3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}

@app.route('/detail/<int:nid>',methods=['GET'])
def detail(nid):
    user = session.get('user_info')
    if not user:
        return redirect('/login')

    info = USERS.get(nid)
    return render_template('detail.html',info=info)


@app.route('/index',methods=['GET'])
def index():
    user = session.get('user_info')
    if not user:
        # return redirect('/login')
        url = url_for('l1')
        return redirect(url)
    return render_template('index.html',user_dict=USERS)


@app.route('/login',methods=['GET','POST'],endpoint='l1')
def login():
    if request.method == "GET":
        return render_template('login.html')
    else:
        # request.query_string
        user = request.form.get('user')
        pwd = request.form.get('pwd')
        if user == 'lqz' and pwd == '123':
            session['user_info'] = user
            return redirect('http://www.baidu.com')
        return render_template('login.html',error='用户名或密码错误')

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

detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>详细信息 {{info.name}}</h1>
    <div>
        {{info.text}}
    </div>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户列表</h1>
    <table>
        {% for k,v in user_dict.items() %}
        <tr>
            <td>{{k}}</td>
            <td>{{v.name}}</td>
            <td>{{v['name']}}</td>
            <td>{{v.get('name')}}</td>
            <td><a href="/detail/{{k}}">查看详细</a></td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户登录</h1>
    <form method="post">
        <input type="text" name="user">
        <input type="text" name="pwd">
        <input type="submit" value="登录"><span style="color:red">{{error}}</span>
    </form>
</body>
</html>

Small summary:

flask routing

@app.route('/login')
def login():
    pass

Routing parameters

@app.route('/login',methods=['GET','POST'],endpoint='l1')
def login():
    pass
'''
第一个参数:url
第二个参数:支持的请求方法
第三个参数:endpoint反向解析,不写默认为函数名,并且不能重名,重名就报错
'''

Dynamic routing

@app.route('/index')
def login():
    pass
@app.route('/index/<name>')
def index(name):
    pass
@app.route('/index/int:<nid>')
def index(nid):
    pass

Get the submitted data

from flask import request
@app.route('/index')
def login():
    request.args # GET请求传递的参数
    request.form # POST请求传递的参数

Return data

@app.route('/index')
def login():
    return render_template('模板文件')
	return jsonify(...)	# 传递json格式的数据
	return redirect('/index/') # 传递url
	return redirect(url_for('别名'))	# 别名
	return '字符串'

Template processing

{{值}}
{%for item in list %}
	{{item}}
{% endfor %}
{# #}  #注释

Three, configuration file

1. Default configuration

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:

 {
        'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
        'TESTING':                              False,                          是否开启测试模式
        'PROPAGATE_EXCEPTIONS':                 None,                          
        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
        'SECRET_KEY':                           None,
        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
        'USE_X_SENDFILE':                       False,
        'LOGGER_NAME':                          None,
        'LOGGER_HANDLER_POLICY':               'always',
        'SERVER_NAME':                          None,
        'APPLICATION_ROOT':                     None,
        'SESSION_COOKIE_NAME':                  'session',
        'SESSION_COOKIE_DOMAIN':                None,
        'SESSION_COOKIE_PATH':                  None,
        'SESSION_COOKIE_HTTPONLY':              True,
        'SESSION_COOKIE_SECURE':                False,
        'SESSION_REFRESH_EACH_REQUEST':         True,
        'MAX_CONTENT_LENGTH':                   None,
        'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
        'TRAP_BAD_REQUEST_ERRORS':              False,
        'TRAP_HTTP_EXCEPTIONS':                 False,
        'EXPLAIN_TEMPLATE_LOADING':             False,
        'PREFERRED_URL_SCHEME':                 'http',
        'JSON_AS_ASCII':                        True,
        'JSON_SORT_KEYS':                       True,
        'JSONIFY_PRETTYPRINT_REGULAR':          True,
        'JSONIFY_MIMETYPE':                     'application/json',
        'TEMPLATES_AUTO_RELOAD':                None,
    }

2. Multiple configuration methods

method one:

app.config['DEBUG'] = True
PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)

Way two:

#通过py文件配置
app.config.from_pyfile("python文件名称")
如:
settings.py
DEBUG = True

app.config.from_pyfile("settings.py")
#通过环境变量配置
app.config.from_envvar("环境变量名称")
#app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
环境变量的值为python文件名称名称,内部调用from_pyfile方法

app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads

app.config.from_mapping({'DEBUG': True})
字典格式

app.config.from_object("python类或类的路径")

settings.py(Most used)

app.config.from_object('pro_flask.settings.TestingConfig')

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


class ProductionConfig(Config):
    DATABASE_URI = 'mysql://[email protected]/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目录(Flask对象init方法的参数)

Four, routing system

1. Basic definition of routing

  • Note: The names of routes and views must be globally unique and cannot be repeated, otherwise an error will be reported.
# 指定访问路径为 demo
@app.route(rule='/demo')
def demo():
    return "demo"

2. Typical writing

@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')

3. Receiving routing parameters

  • Tip: The routing parameters are part of the url path.

3.1 Arbitrary routing parameter reception

# 不限定类型的路由参数传递
@app.route(rule='/user/<id>')
def user(id):  # 接受参数
    return "id=%s的用户中心" % id


# 路由参数理论上可以存在多个
# 参数名建议不要使用单字母,因为有些单字母在框架中默认被占用了。
@app.route(rule='/user1/<id>/<page>')
def user1(id, page):  # 接受参数
    return "id=%s,page=%s" % (id, page)

3.2 Restricted routing parameter reception: converter

The specific usage of the converter that comes with the system is written in the comment code of each converter. Please pay attention to the initialization parameters of each converter.

Converter namedescription
stringDefault type, accept any text without slash
intAccept positive integers
floatAccept positive floating point values
pathAccept stringbut also accept slash
uuidAccept UUID (Universal Unique Identifier) ​​string xxxx-xxxx-xxxxx-xxxxx
# 限定类型的路由参数传递
# 路由格式:<类型:参数名>
# 路由参数的类型,flask支持 int整型,float浮点数,path路径,uuid唯一识别码
@app.route(rule='/user2/<int:id>')
def user2(id):
    print('type(id), id:', type(id), id)  # type(id), id: <class 'int'> 1
    return f'<mark>Hello {id}!</make>'


@app.route(rule='/user3/<string:id>')
def user3(id):
    print('type(id), id:', type(id), id)  # type(id), id: <class 'str'> zcdsb123
    return f'<mark>Hello {id}!</make>'


@app.route(rule='/user4/<float:id>')
def user4(id):
    print('type(id), id:', type(id), id)  # type(id), id: <class 'float'> 1.1
    return f'<mark>Hello {id}!</make>'


@app.route(rule='/user5/<path:id>')
def user5(id):
    print('type(id), id:', type(id), id)  # type(id), id: <class 'str'> lxdsb/zcdsb
    return f'<mark>Hello {id}!</make>'


@app.route(rule='/user6/<uuid:id>')
def user6(id):
    print('type(id), id:', type(id), id)  # type(id), id: <class 'uuid.UUID'> 95db2e6c-e7a7-11ea-9ca3-48ba4e4e6384
    return f'<mark>Hello {id}!</make>'

3.3 Default converter configuration

To limit the types of routing parameters, the flask system comes with a converter written in the file werkzeug.routing.py. You can see the following dictionaries at the bottom:

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

4. The essence of routing system

  • Flask routing is based on decorators
  • The execution process of the decorator: without parentheses, pass the function under the decorator to the decorator (function) for execution, get the result, and then assign it to the function below
  • The essence of Flask routing isapp.add_url_rule()
"""
1. decorator = app.route('/',methods=['GET','POST'],endpoint='n1')
    def route(self, rule, **options):
        # app对象
        # rule= /
        # options = {methods=['GET','POST'],endpoint='n1'}
        def decorator(f):
            endpoint = options.pop('endpoint', None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator
2. @decorator
    decorator(index)
"""
#同理
def login():
    return '登录'
app.add_url_rule('/login', 'n2', login, methods=['GET',"POST"])

#与django路由类似
#django与flask路由:flask路由基于装饰器,本质是基于:add_url_rule
#add_url_rule 源码中,endpoint如果为空,endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)

Simple example:

from flask import Flask

app = Flask(__name__)
app.config['DEBUG'] = False


def index():
    return 'index'

# Flask路由本质就是`app.add_url_rule()`
app.add_url_rule('/', endpoint='index', view_func=index)
if __name__ == '__main__':
    app.run()

5. App.add_url_rule parameter introduction

# 提示: 即是@app.route的参数, 本质也是app.add_url_rule的参数
rule='/index'          # 符合URL规则的路由
view_func=index        # 视图函数名称
defaults=None          # 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}可以为函数提供默认参数
endpoint=None          # 名称,用于反向生成URL,即: url_for('名称')
methods=None           # 允许的请求方式,如:["GET", "post"] 大小写都可以, 内部会执行upper()

strict_slashes=None    # 对URL最后的 / 符号是否严格要求, 默认严格. False就是不严格
    @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>')
    
subdomain = None        # 子域名访问
    # C:\Windows\System32\drivers\etc\hosts
    127.0.0.1       www.liuqingzheng.com
    127.0.0.1       admin.liuqingzheng.com
    127.0.0.1       buy.liuqingzheng.com
    
    # 示例
    from flask import Flask, views, url_for
    
    app = Flask(import_name=__name__)
    app.config['SERVER_NAME'] = 'liuqingzheng.com:5000'
    
    @app.route("/", subdomain="admin")
    def static_index():
        return "static.your-domain.tld"
        
    # 可以传入任意的字符串,如传入的字符串为aa,显示为 aa.liuqingzheng.com
    @app.route("/dynamic", subdomain="<username>")
    def username_index(username):
        return username + ".your-domain.tld"
        
    if __name__ == '__main__':
        app.run()
        
    # 支持访问的路由需要包含如下的域名访问格式:www dynamic admin
    http://www.liuqingzheng.com:5000/dynamic
    http://admin.liuqingzheng.com:5000/dynamic
    http://buy.liuqingzheng.com:5000/dynamic

6. Routing support regular: custom converter

The basic steps:

  1. Import converter base class: In Flask, all routing matching rules are recorded using converter objects
  2. Write a custom converter: custom class inheritance and converter base class
  3. Registration: add converter to the default converter dictionary
  4. Use custom converters to implement custom matching rules

6.1GL mode

from flask import Flask, request

# 初始化
app = Flask(import_name=__name__)


# 编写路由视图
@app.route(rule='/')
def index():
    return "<h1>hello world!</h1>"


# 关于路由参数的限制,flask内置的类型不够具体,在开发中,我们经常接受参数,需要更加精确的限制
# 这时候,可以使用正则匹配路由参数
# 正则匹配路由参数,其实就是扩展flask内置的路由限定类型,需要完成4个步骤
# 1. 引入flask的路由转换器
from werkzeug.routing import BaseConverter


# 2. 创建自定义路由转换器
class MobileConverter(BaseConverter):
    """手机号码类型限制"""

    def __init__(self, map, *args):
        super().__init__(map)
        self.regex = "1[3-9]\d{9}"


# 3. 把自定义转换器添加到flask默认的转换器字典中,也就是和原来的int,float等放在一块
app.url_map.converters['mob'] = MobileConverter


# 4. 类似原来的路由参数限制一样,调用自定义转换器名称即可
@app.route(rule='/user/<mob:mobile>')
def user(mobile):
    return mobile


# 1. 引入flask的路由转换器
from werkzeug.routing import BaseConverter


# 2. 创建自定义路由转换器
class RegexConverter(BaseConverter):
    """根据正则进行参数限制"""

    def __init__(self, map, *args):
        super().__init__(map)
        self.regex = args[0]  # args=(\[email protected]\w+\.\w+, )


# 3. 把自定义转换器添加到flask默认的转换器字典中,也就是和原来的int,float等放在一块
app.url_map.converters['re'] = RegexConverter


# 4. 类似原来的路由参数限制一样,调用自定义转换器名称即可
@app.route(rule='/user/<re("\[email protected]\w+\.\w+"):email>')
def user2(email):
    print(app.url_map)  # 获取所有的路由列表
    return email


# 声明和加载配置
class Config():
    DEBUG = True


app.config.from_object(Config)

if __name__ == '__main__':
    # 运行flask
    app.run(port=8000)

6.2 LQZ mode

# 流程: 
"""
1. 写类,继承BaseConverter
2. 注册:app.url_map.converters['regex'] = RegexConverter
3. 使用:@app.route('/index/<regex("\d+"):nid>')  正则表达式会当作第二个参数传递到类中
"""

from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)


class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式
    """

    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时,匹配成功后传递给视图函数中参数的值
        """
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        """
        val = super(RegexConverter, self).to_url(value)
        return val


# 添加到flask中
app.url_map.converters['regex'] = RegexConverter


@app.route('/index/<regex("\d+"):nid>')
def index(nid):
    print(url_for('index', nid='888'))
    return 'Index'


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

Five, CBV source code analysis

django中的三件套:
			Httpresponse   ''
			render         render_template
			redirect       redirect
			JsonResponse   jsonify


def auth(func):
    def inner(*args, **kwargs):
        print('before')
        result = func(*args, **kwargs)
        print('after')
        return result

    return inner

class IndexView(views.View):
    methods = ['GET']
    decorators = [auth, ]

    def dispatch_request(self):
        print('Index')
        return 'Index!'

app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint
#或者,通常用此方式
  class IndexView(views.MethodView):
            methods = ['GET']
            decorators = [auth, ]

            def get(self):
                return 'Index.GET'

            def post(self):
                return 'Index.POST'
        app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))  # name=endpoint

Six, template

1. Rendering variables

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户列表</h1>
    <table>
        {% for k,v in user_dict.items() %}
        <tr>
            <td>{{k}}</td>
            <td>{{v.name}}</td>
            <td>{{v['name']}}</td>
            <td>{{v.get('name')}}</td>
            <td><a href="/detail/{{k}}">查看详细</a></td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

2. Variable loop

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户列表</h1>
    <table>
        {% for k,v in user_dict.items() %}
        <tr>
            <td>{{k}}</td>
            <td>{{v.name}}</td>
            <td>{{v['name']}}</td>
            <td>{{v.get('name')}}</td>
            <td><a href="/detail/{{k}}">查看详细</a></td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

3. Logical judgment

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户列表</h1>
    <table>
        {% if name %}
          <h1>Hello {{ name }}!</h1>
        {% else %}
          <h1>Hello World!</h1>
        {% endif %}
    </table>
</body>
</html>

4. Deal with XSS attacks

app.py

from flask import Flask, Markup, render_template

app = Flask(__name__)


def func1(arg):
    # Markup 类似于 Django中的make_save
    return Markup("<input type='text' value='%s' />" % (arg,))


@app.route('/')
def index():
    return render_template('xss.html', ff=func1)


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

xss.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ff('六五')}}
    <!-- safe和Django中过滤器中的safe方法一致 -->
	{{ff('六五')|safe}}
</body>
</html>