Flask,1-2

前言:通过一个简单的用户认证页面来认识flask:涉及到的点如下:

1、app = Flask(__name__)     # app是一个flask类的对象,其中__name__表示当前模块的名字。

2、app.secret_key = "asdasdqw"   # 给session加盐(必须要写,否则报错,session需要加密)。

3、app=Flask(__name__,template_folder=)    # 其中template_folder里面定义了默认目录为"templates",也可以自定义目录名。

4、@app.route("/login",methods=["GET","POST"])     # 不写methods默认只支持GET方法,其他PUT,DELETE都可以继续加。

5、request.form 获取POST传过来的值,request.args 获取GET传过来的值。

6、render_template("datail.html", info=info) # 把info传递给前端,前端接收为{{info}}。

7、user = request.form.get("user")  # 创建一个session个一个用户。

8、user_info = session.get("user_info") #获取session的值。

一、Python 代码部分:
from flask import Flask,render_template,request,redirect,session

app = Flask(__name__)     # app是一个flask类的对象,
# app=Flask(__name__,template_folder=)         # 其中template_folder里面定义了默认目录为"templates"
app.secret_key = "asdasdqw"   # 给session加盐
app.debug = True
user_dict = {
    "1":{"name":"szq","age":18},
    "2":{"name":"sudada","age":28},
    "3":{"name":"wsp","age":38},
}


# 查看用户详细信息的页面
@app.route("/datail",methods=["GET","POST"])
def datail():
    uid = request.args.get("uid")
    info = user_dict.get(uid)  # 通过字典的key获取对应的value
    return render_template("datail.html", info=info)


# 登录页面
@app.route("/login",methods=["GET","POST"])     # 不写methods默认只支持GET方法,其他PUT,DELETE都可以继续加
def login():
    user_info = session.get("user_info")   # 通过session的值来判断是否登录成功
    if user_info:
        return redirect("/index")
    if request.method == "GET":
        return render_template("login.html")
    user = request.form.get("user")
    pwd = request.form.get("pwd")
    # request.form 获取POST传过来的值
    # request.args 获取GET传过来的值

    if user == "szq" and pwd == "123":
        session["user_info"] = user             # 用户登录成功之后把用户信息放入session里面(默认放在cookie里面),session可以看成是一个字典组成的
        return redirect("/index")
    return render_template("login.html", msg = "用户名或者密码错误") # 通过msg=""把值传递给html页面内的{{msg}}


# 登出页面
@app.route("/logout",methods=["GET","POST"])
def logout():
    del session["user_info"]
    return redirect("/login")


# 登录成功页面
@app.route("/index",methods=["GET"])
def index():
    user_info = session.get("user_info")   # 通过session的值来判断是否登录成功
    if not user_info:
        return redirect("/login")
    return render_template("index.html",user_dict=user_dict)


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



二、HTML 页面部分:
2.1、login.html如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
    <h1>login</h1>
    <form method="post">
        <input type="text" name="user">
        <input type="password" name="pwd">
        <input type="submit" value="提交"> {{msg}}
    </form>
</body>
</html>

2.2、index.html如下:
<body>
    <ul>
        <!-- user_dict.keys()拿到的是字典的key,user_dict.values()拿到的是字典的value, user_dict.items()拿到的是字典 -->
        {% for k,v in user_dict.items() %}
            <li>{{ v.name }} <a href="/datail?uid={{k}}">查看详细</a></li>
        {% endfor %}
    </ul>
</body>

2.3、datail.html如下:
<body>
    <h1>详细信息</h1>
    <div>{{info.name}}</div>
    <div>{{info.age}}</div>
</body>

 

9、flask框架下,各种环境下配置文件的自定义,主要使用:app.config.from_object("settings.Testing") 来实现。

# 一、主体文件内容:
from flask import Flask
app=Flask(__name__)
app.config.from_object("settings.Testing")
# 从settings.py文件里面找到Testing类[将类里面所有的静态字段(大写)拿到并放入到(当前文件)配置里面去]

@app.route("/index",methods=["GET","POST"])
def index():
    return "index"

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


# 二、settings.py 文件配置如下:
class BaseConfig():
    DEBUG = True
    SECRET_KEY = "jkoiwmnsiuhvlkw"


class ProductionConfig(BaseConfig):
    DATABASE_URI="mysql://user@prod/foo"
    DEBUG = False


class DevelopmentConfig(BaseConfig):
    DATABASE_URI = "mysql://user@dev/foo"
    pass


class Testing(BaseConfig):
    DATABASE_URI = "mysql://user@test/foo"
    pass

 

一、Flask框架之创建路由的2种方式

1.1、通过 route("/index") 的方式  #直接写路由名称(一般都是有这种)

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

@app.route("/index",methods=["GET","POST"])                       
def index():        
    return "index"

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

 

1.2、通过 app.add_url_rule("/order",view_func=order) 的方式  #写路由名称加指定对应的响应函数

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

def order():
    return "order"
app.add_url_rule("/order",view_func=order)   # view_func后面传入函数名

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

 

二、Flask框架之反向生成URL(获取请求URL的值,并给这个值起一个别名,这个别名可以直接被redirect使用)

2.1、两种方式

1.通过 endpoint 的方式定义一个别名(目的是为了通过这个别名获取请求URL的值);

2.使用 url_for 默认传入函数名的方式获取请求URL的值(不需要endpoint定义别名了)。

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

# endpoint="n1"  获取请求的URL的值(或者说是给URL起一个别名)
@app.route("/index",methods=["GET","POST"],endpoint="n1")
def index():
    v1 = url_for("n1")     # 1.通过 endpoint 的方式定义一个别名"n1",n1拿到值就是"/index"
    v2 = url_for("login")  # 2.使用 url_for 默认传入函数名的方式获取请求URL的值"/login"(不需要endpoint定义别名了)。
    print(v1,v2)           # /index /login
    return redirect(v2)    # 别名可以直接当做URL来使用

@app.route("/login",methods=["GET","POST"])
def login():
     return "login"

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

 

三、Flask框架之自定义路由转换器

3.1、路由系统:
<int:nid>意思:index函数将被传递一个参数(int类型),传递的值为"nid"。
<username>意思:index函数将被传递一个参数(str类型),传递的值为"username"。
<float>意思:index函数将被传递一个参数(float类型),传递的值为"float"。
<path:path>意思:index函数将被传递一个参数(path类型),传递的值为path(假如访问路径为:/index/abc/def/ang,那么path的值为:abc/def/ang)。

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

# 路由系统:例子如下
# <int:nid>意思:index函数将被传递一个参数(int类型),传递的值为"nid"。
# <username>意思:index函数将被传递一个参数(str类型),传递的值为"username"。
# <float>意思:index函数将被传递一个参数(float类型),传递的值为"float"。
# <path:path>意思:index函数将被传递一个参数(path类型),传递的值为path(假如访问路径为:/index/abc/def/ang,那么path的值为:abc/def/ang)。

@app.route("/index/<int:nid>",methods=["GET","POST"])
def index(nid):
    print(nid)     # 请求URL:http://127.0.0.1:5000/index/111,获取到的值为 "111"
    return "index"

@app.route("/index/<username>",methods=["GET","POST"])
def index(username):
    print(username)     # 请求URL:http://127.0.0.1:5000/index/aaaa,获取到的值为 "aaaa"
    return "index"

@app.route("/index/<float>",methods=["GET","POST"])
def index(float):
    print(float)     # 请求URL:http://127.0.0.1:5000/index/1.3,获取到的值为 "1.3"
    return "index"

@app.route("/index/<path:path>",methods=["GET","POST"])
def index(path):
    print(path)     # 请求URL:http://127.0.0.1:5000/index/a/b/c,获取到的值为 "a/b/c"
    return "index"

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

 

四、Flask框架之 app.route 参数

4.1、strict_slashes=True|False:决定URL结尾可不可以带 / 号。

from flask import Flask, render_template, redirect, request, url_for

app = Flask(__name__)
app.debug = True

# strict_slashes=False,URL最后的 / 符号可带可不带。
@app.route("/index", methods=["GET", "POST"], strict_slashes=False)
def index():
    return "index"

# strict_slashes=True,URL最后的 / 符号不能带。
@app.route("/login", methods=["GET", "POST"], strict_slashes=True)
def login():
    return "login"

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

4.2、重定向URL:301跳转:redirect_to="/new"   302跳转:return redirect("/order")

方式一:301
from flask import Flask, render_template, redirect, request, url_for
app=Flask(__name__)
app.debug=True

# 2、redirect_to="/new":重定向URL到"/new"
@app.route("/index",methods=["GET","POST"],redirect_to="/new")
def index():
    return "index"

@app.route("/new",methods=["GET","POST"])
def new():
    return "new"

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

#===============================分割线================================

方式二:302
from flask import Flask, request, redirect, render_template
app=Flask(__name__)
app.debug=True

@app.route("/index",methods=["GET","POST"])
def index():
    return redirect("/order")


@app.route("/order",methods=["GET","POST"])
def order():
    return "order"

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

 

五、Flask框架之获取子域名的路由

5.1、通过 subdomain="<username>"  获取2级域名。

# 本地hosts解析:
127.0.0.1       localhost
127.0.0.1       sudada.com
127.0.0.1       www.sudada.com
127.0.0.1       sale.sudada.com
127.0.0.1       buy.sudada.com

# 代码如下:
from flask import Flask, render_template, redirect, request, url_for
app=Flask(__name__)
app.debug=True

app.config['SERVER_NAME'] = 'sudada.com:5000'   # 这里指定主域名地址+端口。

@app.route("/dynamic",subdomain="<username>")   # 域名请求"/dynamic"时获取请求URL的2级域名。
def index(username):
    print(username)
    return "dynamic"

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

# 访问www.sudada.com:5000/dynamic,获取到的值为"www"

 

六、Flask框架之视图中添加装饰器

6.1、使用 @functools.wraps(func) 方式,将inner函数伪造成被传值过来的func函数。

from flask import Flask
app=Flask(__name__)
app.debug=True
import functools

# 为什么要有@functools.wraps(func)?
# 1.由于endpoint默认传入的是函数名,而index和order函数已经被wapper装饰了。
#   此时的index=wapper(index),真正执行的函数为wapper装饰器内的inner函数,所以在index函数内endpoint传入的就是inner函数。
# 2.当index,order都被wapper装饰了之后,index函数内的endpoint传入的是inner函数,
#   order函数内的endpoint传入的也是inner函数,同一个inner函数传入2次就报错。
# 3.所以要在wapper装饰器内使用"@functools.wraps(func)",同时在"@wapper"要写在"@app.route("/dynamic")"的后面
# [email protected](func)的目的就是将inner函数伪造成被传值过来的func函数。
def wapper(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        print(inner.__name__)            # 查看装饰器内inner函数的名字是"inner",还是被传递过来的"func"函数。
        return func(*args,**kwargs)
    return inner


# 如果wapper装饰器内不加"@functools.wraps(func)"的话,
# 1、请求"/dynamic"实际上访问的是装饰器内的inner函数。
# 2、代码运行报错,提示装饰器内的inner已经存在。
@app.route("/dynamic",methods=["GET","POST"])
@wapper
def dynamic():
    return "dynamic"


@app.route("/order",methods=["GET","POST"])
@wapper
def order():
    return "order"


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

 

6.2、Flask框架之常见装饰器之404报错自定义,@app.errorhandler(404) 

  访问当前视图输入的内容不存在时,报错的404为自定义的输出。

# 在视图里面加一个"@app.errorhandler(404)"
@app.errorhandler(404)
def page_not_find(error):
    return "this page does not exist",404  # 自定义的404输出

 

七、Flask框架之请求和响应相关用法

import json
from flask import Flask, request, redirect, render_template, jsonify, make_response

app = Flask(__name__)
app.debug = True


@app.route("/index", methods=["GET", "POST"])
def index():
    # 请求相关的常见用法
    # request.method  # 请求方式
    # request.args    # 获取GET请求的数据
    # request.form    # 获取POST请求的数据
    # request.cookies
    # request.headers
    # request.path

    # 上传文件使用
    # request.files
    # obj = request.files["the_file_name"]
    # obj.save("/var/www/uploads/" + secure_filename(obj.filename))

    # 响应相关
    # return jsonify({"aaa":123})   # 通过"jsonify",返回json格式字符串
    # return "index"                # 返回字符串
    # return render_template("index.html",n1=123)  # 返回模板配置文件,并通过n1=123传值
    # return redirect("/order")     # 重定向302

    # 响应相关之设置cookies或者响应头,通过make_response包裹之后,response就可以进行设置cookie和设置head
    # response = make_response(render_template("index.html"))
    # response.delete_cookie("key")                   # 删除cookies
    # response.set_cookie("key","value")              # 设置cookies
    # response.headers["X-Something"] = "A value"     # 设置响应头部的格式
    # return response

    return "index"


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

 

八、Flask框架之模板引擎

8.1、模板调用函数内的数据(字符串,列表,字典,函数)及模板语法传入全局函数

Python部分:
from flask import Flask, request, redirect, render_template, jsonify, make_response, Markup
app=Flask(__name__)
app.debug=True

# 模板语法之传入全局函数(每个模板[*.html]都可以调用的函数)
@app.template_global()
def k6(a1,b1):
    return a1 + b1

def get_input(value):
    return Markup("<input value='%s'>" %value)

# 模板调用
@app.route("/index",methods=["GET","POST"])
def index():

    context={
        "k1":123,
        "k2":[11,22,33],
        "k3":{"name":"sudada","age":24},
        "k4":lambda x:x+1,
        "k5":get_input,
    }
    return render_template("index.html",**context)  # 把字典context作为参数传入


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


HTML部分:
<body>
    <h1>{{k1}}</h1>
    <h1>{{k2[0]}}  {{k2.0}}</h1>
    <!--  或者列表内的数据  -->
    <h1>{{k3["name"]}}  {{k3.name}}  {{k3.get("aaa",999)}}</h1>
    <!--  获取字典内的数据  -->
    <h1>{{k4(666)}}</h1>
    <!--  k4(666)表示给函数传递值666  -->
    <h1>{{k5(99)}}</h1>
    <!-- "|safe"的功能是将"input"标签变成真正的框,不加的话会显示原始字符串,并不会做渲染 -->
    <!--  后端也可以使用"Markup"模块来实现:return Markup("<input value='%s'>" %value)  -->
    <h1>{{k6(1,2)}}</h1>
    <!-- 执行全局函数k6,(1,2)表示传入的值 -->
</body>

 

  8.3、模板继承

父模板:
<body>
    <div>头部</div>
    <div>
        {% block content %} {% endblock %}
    </div>
    <div>底部</div>

</body>


子模板继承:
{% extends "layout.html" %}

{% block content %}

    <h1>{{k1}}</h1>
    <h1>{{k2[0]}}  {{k2.0}}</h1>
    <!--  或者列表内的数据  -->
    <h1>{{k3["name"]}}  {{k3.name}}  {{k3.get("aaa",999)}}</h1>
    <!--  获取字典内的数据  -->
    <h1>{{k4(666)}}</h1>
    <!--  k4(666)表示给函数传递值666  -->
    <h1>{{k5(99)}}</h1>
    <!-- "|safe"的功能是将"input"标签变成真正的框,不加的话会显示原始字符串,并不会做渲染 -->
    <!--  后端也可以使用"Markup"模块来实现:return Markup("<input value='%s'>" %value)  -->
    <h1>{{k6(1,2)}}</h1>
    <!-- 执行全局函数k6,(1,2)表示传入的值 -->

{% endblock %}

 

九、Flask框架之session使用和源码流程

from flask import Flask, session

app = Flask(__name__)
app.debug = True
app.secret_key = "asdimwe"  # session加盐


@app.route("/x1", methods=["GET", "POST"])
def index():
    # 这个session就是去ctx中获取一个session
"""
# Session请求过程步骤解析:
# 前言:flask 请求执行的步骤
    app.__call__()
    app.wsgi_app()

# 1、请求刚到达时:
    ctx = self.request_context(environ) == RequestContext(request,session=None)
    ctx.push()

# 2、执行视图函数

# 3、请求结束,通过save_session将设置的session值写到cookie里面。
    SecureCookieSessionInterface.save_session()
"""

    session["k1"] = 123  # 给"k1"这个key赋值123
    return "index"


@app.route("/x2", methods=["GET", "POST"])
def order():
    print(session["k1"])  # 获取"k1"对应的值(value)
    return "order"


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

 

十、Flask框架之 before_reqeust 和 after_request

10.1、before_reqeust内不带有return返回值时

   before_reqeust:请求到达视图函数@app.route("/x1")之前,执行的代码(先进先出)

   after_request:视图函数@app.route("/x1")执行完毕之后,执行的代码(先进后出)

from flask import Flask,session
app=Flask(__name__)
app.debug=True
app.secret_key="asdimwe"   # session加盐


# 请求到达视图函数之前,执行的代码(先进先出)
@app.before_request
def xxx1():
    print("执行前1")

@app.before_request
def xxx2():
    print("执行前2")


# 视图函数执行完毕之后,执行的代码(先进后出)
@app.after_request
def ooo1(response):
    print("执行后1")
    return response

@app.after_request
def ooo2(response):
    print("执行后2")
    return response


#视图函数
@app.route("/x1",methods=["GET","POST"])
def x1():
    print("视图函数x1")
    return "视图函数x1"

@app.route("/x2",methods=["GET","POST"])
def x2():
    print("视图函数x2")
    return "视图函数x2"

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

# 执行结果:
执行前1
执行前2
视图函数x1
执行后2
执行后1

       

10.2、before_reqeust内带有return返回值时

如果执行前的函数带有return返回值的话,那么将return的值作为结果返回到前端页面,同时不再执行视图函数。并且把执行后的函数执行完。

from flask import Flask
app=Flask(__name__)
app.debug=True


# 请求到达视图函数之前,执行的代码(先进先出)
@app.before_request
def xxx1():
    print("执行前1")
    return "执行前1_stop"

@app.before_request
def xxx2():
    print("执行前2")
    return "执行前2_stop"


# 视图函数执行完毕之后,执行的代码(先进后出)
@app.after_request
def ooo1(response):
    print("执行后1")
    return response

@app.after_request
def ooo2(response):
    print("执行后2")
    return response


#视图函数
@app.route("/x1",methods=["GET","POST"])
def x1():
    print("视图函数x1")
    return "视图函数x1"

@app.route("/x2",methods=["GET","POST"])
def x2():
    print("视图函数x2")
    return "视图函数x2"

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


# 执行结果:
执行前1
执行后2
执行后1

 

10.3、基于 before_reqeust 实现用户登录

from flask import Flask,session,request,redirect,render_template
app=Flask(__name__)
app.debug=True
app.secret_key="asdimwe"   # session加盐


# 执行视图函数之前,执行before_request
# 用户登录认证,通过session来判断用户是否登录,如果Session不存在则判断为未登录需要重新登录。
@app.before_request
def check_login():
    
    # 如果输入的URL不是以"login"开头的,全部都获取一次session判断用户是否登录。
    if request.path != "/login": 
        user=session.get("user_info")
        
        # 如果用户未登录,则跳转到登录页面
        if not user: 
            return redirect("/login")


# 登录的视图函数
@app.route("/login",methods=["GET","POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    user=request.form.get("user")
    pwd=request.form.get("pwd")
    if user == "szq" and pwd == "123":
        session["user_info"]=user
        return redirect("/index")
    else:
        return render_template("login.html", msg="用户名或密码错误")  # msg关联"login.html"里面的{{msg}}

@app.route("/index",methods=["GET","POST"])
def index():
    return "首页"

@app.route("/xxx",methods=["GET","POST"])
def xxx():
    return "xxx"

# 删除session
@app.route("/logout", methods=["GET", "POST"])
def logout():
    del session["user_info"]
    return "logout"

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

 

十一、Flask框架之闪现

方式一:添加单个数据
from flask import Flask,flash,get_flashed_messages
app=Flask(__name__)
app.debug=True
app.secret_key="asdkbmn"

@app.route("/x1",methods=["GET","POST"])
def x1():
    # 添加一条数据到列表里(做一次GET请求就会添加一条数据到列表里面,可做多次get请求),GET:http://127.0.0.1:5000/x1
    flash("我要上天1")
    return "x1"

@app.route("/x2",methods=["GET","POST"])
def x2():
    # 获取这个数据,GET:http://127.0.0.1:5000/x2,数据获取完毕之后再次获取的时候,数据就没了。
    data = get_flashed_messages()
    print(data)    # "我要上天1"
    return "x2"

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

方式二:添加多个数据
from flask import Flask,flash,get_flashed_messages
app=Flask(__name__)
app.debug=True
app.secret_key="asdkbmn"

@app.route("/x1",methods=["GET","POST"])
def x1():
    # 添加一条数据,定义一个key(做一次GET请求,可做多次get请求),GET:http://127.0.0.1:5000/x1
    flash("我要天1",category="x1")
    flash("我要天2",category="x2")
    return "x1"

@app.route("/x2",methods=["GET","POST"])
def x2():
    # 获取这个数据,指定对应的key,GET:http://127.0.0.1:5000/x2,获取完毕之后在做一次GET请求,数据就没了。
    data_x1 = get_flashed_messages(category_filter=["x1"])
    data_x2 = get_flashed_messages(category_filter=["x2"])
    print(data_x1)   # ['我要天1']
    print(data_x2)   # ['我要天2']
    return "x2"

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

 

十二、蓝图

12.1、三大作用

1、目录结构的划分    2、URL的划分      3、基于蓝图内的视图单独实现befor_request

 

12.2、蓝图基本的目录结构如下:

 

12.3、代码示例

一、manage.py文件
from blueview import app

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


二、__init__.py文件
from flask import Flask
app=Flask(__name__)
app.debug=True

from .views import account
from .views import admin
from .views import user

app.register_blueprint(account.ac,url_prefix="/account")
app.register_blueprint(admin.ad,url_prefix="/admin")
app.register_blueprint(user.us,url_prefix="/user")


三、account.py文件
from flask import Blueprint,render_template
ac=Blueprint("ac",__name__)

@ac.route("/login")
def login():
    return render_template("login.html")

@ac.route("/logout")
def logout():
    return "logout"


四、user.py文件
from flask import Blueprint
us=Blueprint("us",__name__)
# url_prefix="/user": 访问当前代码内所有的视图函数,请求的URL钱必须带有"/user"。
# 还可以通过static_folder=,和template_folder=单独制定目录。
# 目录的优先级顺序为: 优先去__init__内查找对应的目录,若目录不存在则去蓝图内找当前定义的目录。


@us.route("/info")
def info():
    return "info"


五、admin.py文件
from flask import Blueprint

ad=Blueprint("ad",__name__)

@ad.route("/home")
def home():
    return "home"

 

十三、pipreqs

功能:到项目里边,自动找到项目里面依赖了哪些模板,并且找到模块对应的版本,然后生成一个文件"requirements.txt"。

使用方法:

    1、安装命令:pip3 install pipreqs

    2、生成依赖文件(进入到项目目录下):pipreqs ./

    3、安装依赖文件:pip3 install -r requirements.txt

 

十四、延伸部分--什么是函数?什么是方法?如何区分。

  举例说明:

from types import MethodType,FunctionType

class Foo(object):
    def fetch(self):
        pass

# 类直接调用函数,它就是函数
print(isinstance(Foo.fetch,MethodType))    # False
print(isinstance(Foo.fetch,FunctionType))  # True


# 实例化类之后,通过对象去调用方法(函数),它就是方法
obj=Foo()
print(isinstance(Foo.fetch,MethodType))    # True
print(isinstance(Foo.fetch,FunctionType))  # False
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章