Flask-数据库 *** 作

Flask-数据库 *** 作,第1张

概述一、什么是ORM ORM 全拼Object-Relation Mapping. 称为对象-关系映射 主要实现模型对象到关系数据库数据的映射. 比如:把数据库表中每条记录映射为一个模型对象 关系映射 二

目录一、什么是ORM二、Flask-SQLAlchemy安装及设置1. 安装2. 数据库连接设置三、数据库基本 *** 作1. 增删改2. 查询3. 查询练习四、综合案例-图书管理1. 图书馆测试数据显示2. 图书馆添加数据3. 图书馆删除书籍4. 图书馆删除作者5. 图书馆CSRFProtect应用6. 表单的创建7. library.html五、多对多1. 需求分析2. 代码六、数据库迁移

一、什么是ORMORM 全拼Object-Relation MapPing. 称为对象-关系映射主要实现模型对象到关系数据库数据的映射.比如:把数据库表中每条记录映射为一个模型对象

关系映射

二、Flask-sqlAlchemy安装及设置1. 安装安装 flask-sqlalchemy
pip install flask-sqlalchemy
如果连接的是 MysqL 数据库,需要安装 MysqLdb
pip install flask-MysqLdb

提示: 如果flask-MysqLdb安装不上,推荐安装,pip install pyMysqL

2. 数据库连接设置设置数据库的链接地址,追踪信息格式:MysqL://<用户名>:<密码>@:<端口>/数据库名称
# 数据库链接地址app.config['sqlALCHEMY_DATABASE_URI'] = 'MysqL://root:MysqL@127.0.0.1:3306/test'# 动态追踪修改设置,如未设置只会提示警告app.config['sqlALCHEMY_TRACK_MODIFICATIONS'] = True

查看映射的SQL语句,设置: app.config['sqlALCHEMY_ECHO'] = True

配置完成需要去 MysqL 中创建项目所使用的数据库
$ MysqL -uroot -pMysqL$ create database test charset utf8;
from flask import Flaskfrom flask_sqlalchemy import sqlAlchemyapp = Flask(__name__)#2.设置数据库的配置信息#设置数据库的链接信息,app.config["sqlALCHEMY_DATABASE_URI"] = "MysqL+pyMysqL://root:123456@127.0.0.1:3306/test"#该字段增加了大量的开销,会被禁用,建议设置为Falseapp.config["sqlALCHEMY_TRACK_MODIFICATIONS"] = False#3.创建sqlalchemy对象db,关联appdb = sqlAlchemy(app)# 4.编写模型类,字段,继承自db.Modelclass Student(db.Model):    __tablename__ = "students"    #主键,参数1: 表示ID的类型,参数2: 表示ID的约束类型    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))@app.route('/')def hello_world():    return "helloworld"if __name__ == '__main__':    #删除继承自db.Model的表    db.drop_all()    #5.创建数据库的表,创建的是继承自db.Model的表    db.create_all()    app.run(deBUG=True)
三、数据库基本 *** 作在Flask-sqlAlchemy中,插入、修改、删除 *** 作,均由数据库会话管理。会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 db.session.commit() 方法提交会话。在 Flask-sqlAlchemy 中,查询 *** 作是通过 query 对象 *** 作数据。最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。

1. 增删改
"""增删改- 全部都是使用db.session *** 作- 常见方法:  - db.session.add(obj) 添加单个对象  - db.session.add_all([obj1,obj2]) 添加多个对象  - db.session.delete(obj) 删除单个对象  - db.session.commit() 提交会话  - db.drop_all() 删除继承自db.Model所有表  - db.create_all() :创建继承自db.Model的所有表  - 其他:    - db.session.rollback() 回滚    - db.session.remove() 移除会话  - 案例: 编写两个模型类,一个角色模型类,还有一个用户模型类    - 关系: 一对多"""from flask import Flaskfrom flask_sqlalchemy import sqlAlchemyapp = Flask(__name__)#1.设置数据库的配置信息app.config["sqlALCHEMY_DATABASE_URI"] = "MysqL+pyMysqL://root:123456@127.0.0.1:3306/test"app.config["sqlALCHEMY_TRACK_MODIFICATIONS"] = False#2.创建sqlalchemy对象,关联appdb = sqlAlchemy(app)#3.编写模型类#角色(一方)class Role(db.Model):    __tablename__ = "roles"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    #如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<Role:%s>"%self.name#用户(多方)class User(db.Model):    __tablename__ = "users"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    #建立外键    role_ID = db.Column(db.Integer,db.ForeignKey(Role.ID))    #如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<User:%s>"%self.name@app.route('/')def hello_world():    return "helloworld"if __name__ == '__main__':    #为了演示方便,先删除表,后创建    db.drop_all()    db.create_all()    app.run(deBUG=True)

使用ipython进行测试,前提是先进行安装

2. 查询
# -*- Coding = utf-8 -*-# @Time : 2020/10/2 10:15# @Author : md'''查询练习'''from flask import Flaskfrom flask_sqlalchemy import sqlAlchemyapp = Flask(__name__)# 1.设置数据库的配置信息app.config["sqlALCHEMY_DATABASE_URI"] = "MysqL+pyMysqL://root:123456@127.0.0.1:3306/test"app.config["sqlALCHEMY_TRACK_MODIFICATIONS"] = False# app.config["sqlALCHEMY_ECHO"] = True# 2.创建sqlalchemy对象,关联appdb = sqlAlchemy(app)# 3.编写模型类# 角色(一方)class Role(db.Model):    __tablename__ = "roles"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    # 关系数据写在这个表中,也就是写在一方这个表中    # 为了查询方便,不会产生实体字段    # 给role添加了一个users属性,那么查询的方式是,role.users    # 给user添加了一个role属性,user.role    # 重点 建立关系,这里的名字为想要建立关系的模型名字    # 解释:前半句话是给Role(本模型)模型添加一个users属性,因为这两个数据库通外键连接    # 所以,可以通过这个表的对象,就可以访问User表中的数据,例如:查看角色是admin的所有用户role.users    # 后半句是给本(自己)模型添加一个role属性,这样User表中的对象,就可以访问本表中的数据,可以知道某个用户是什么身份    # 例如查看用户的身份,user.role    # lazy="dynamic" 是懒加载    # backref反向引用    users = db.relationship("User",backref="role",lazy="dynamic")    # 如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<Role:%s>" % self.name# 用户(多方)class User(db.Model):    __tablename__ = "users"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    email = db.Column(db.String(32))    password = db.Column(db.String(32))    # 建立外键    role_ID = db.Column(db.Integer,db.ForeignKey(Role.ID))    # 如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<User:%s,%s,%s>" % (self.ID,self.name,self.email,self.password)@app.route('/')def hello_world():    return "helloworld"if __name__ == '__main__':    # 为了演示方便,后创建    db.drop_all()    db.create_all()    # 创建测试数据    ro1 = Role(name='admin')    db.session.add(ro1)    db.session.commit()    # 再次插入一条数据    ro2 = Role(name='user')    db.session.add(ro2)    db.session.commit()    # 多条用户数据    us1 = User(name='wang',email='wang@163.com',password='123456',role_ID=ro1.ID)    us2 = User(name='zhang',email='zhang@189.com',password='201512',role_ID=ro2.ID)    us3 = User(name='chen',email='chen@126.com',password='987654',role_ID=ro2.ID)    us4 = User(name='zhou',email='zhou@163.com',password='456789',role_ID=ro1.ID)    us5 = User(name='tang',email='tang@itheima.com',password='158104',role_ID=ro2.ID)    us6 = User(name='wu',email='wu@gmail.com',password='5623514',role_ID=ro2.ID)    us7 = User(name='qian',email='qian@gmail.com',password='1543567',role_ID=ro1.ID)    us8 = User(name='liu',email='liu@itheima.com',password='867322',role_ID=ro1.ID)    us9 = User(name='li',email='li@163.com',password='4526342',role_ID=ro2.ID)    us10 = User(name='sun',email='sun@163.com',password='235523',role_ID=ro2.ID)    db.session.add_all([us1,us2,us3,us4,us5,us6,us7,us8,us9,us10])    db.session.commit()    app.run(deBUG=True)
其中realtionship描述了Role和User的关系。第一个参数为对应参照的类"User"第二个参数backref为类User,反向引用属性

第三个参数lazy决定了什么时候sqlALchemy从数据库中加载数据

如果设置为子查询方式(subquery),则会在加载完Role对象后,就立即加载与其关联的对象,这样会让总查询数量减少,但如果返回的条目数量很多,就会比较慢设置为 subquery 的话,role.users 返回所有数据列表另外,也可以设置为动态方式(dynamic),这样关联对象会在被使用的时候再进行加载,并且在返回前进行过滤,如果返回的对象数很多,或者未来会变得很多,那最好采用这种方式

User.query.filter().all()

3. 查询练习
查询所有用户数据User.query.filter().all()此时过滤器可以不写User.query.all()查询有多少个用户User.query.count()查询第1个用户User.query.first()查询ID为4的用户[3种方式]User.query.get(4) 此时get里面是主键 User.query.filter(User.ID == 4).all() 返回的是列表,但满足的只有一个User.query.filter(User.ID == 4).first()User.query.filter_by(ID=4).first()查询名字结尾字符为g的所有数据[开始/结尾/包含]User.query.filter(User.name.startswith('g')).all() User.query.filter(User.name.endswith('g')).all()User.query.filter(User.name.contains('g')).all()查询名字不等于wang的所有数据User.query.filter(User.name != 'wang').all()查询名字和邮箱都以 li 开头的所有数据User.query.filter(User.name.startswith('li'),User.email.startswith('li')).all()查询password是 123456 或者 email 以 itheima.com 结尾的所有数据from sqlalchemy import or_User.query.filter(or_(User.password == '123456',User.email.endswith('itheima.com'))).all()查询ID为 [1,3,5,7,9] 的用户列表User.query.filter(User.ID.in_([1,9])).all()查询name为liu,的角色数据user = User.name.filter(User.name == 'liu').first()role = Role.query.filter(Role.ID == user.role_ID).first()查询所有用户数据,并以邮箱排序 User.query.order_by(User.email).all() User.query.order_by(User.email.desc()).all()每页3个,查询第2页的数据#page: 表示要查询的页数#per_page: 表示每页有多少条数据#Error_out: 建议写成False,查不到不会报错paginate = User.query.paginate(page,per_page,Error_out)paginate.pages #总页数paginate.page #当前页paginate.items #当前的对象列表查询前两条数据 User.query.limit(2).all()

lazy="dynamic"

四、综合案例-图书管理

目的:

表单创建数据库 *** 作一对多关系演练

实现步骤:

1.创建数据库配置信息,定义模型类2.创建数据库表,添加测试数据3.编写HTML页面,展示数据4.添加数据5.删除书籍,删除作者1. 图书馆测试数据显示步骤1.查询所有作者信息2.携带作者信息,渲染页面2. 图书馆添加数据添加的逻辑分析:1.如果作者存在,书籍存在,不能添加2.如果作者存在,书籍不存在,可以添加3.如果作者不存在,可以添加3. 图书馆删除书籍步骤1.根据书籍编号获取书籍对象2.删除书籍对象3.重定向到页面展示4. 图书馆删除作者步骤1.根据作者编号获取作者对象2.遍历删除,作者书籍对象3.删除作者,提交数据库4.重定向到页面展示5. 图书馆CSRFProtect应用作用: 防止csrf攻击的使用步骤:1.导入类CSRFProtect2.使用CSRFProtect保护app一旦使用POST,PUT,DELTE,PATCH方式提交的时候就需要校验csrf_token3.需要设置SECRET_KEY,用来加密csrf_token4.设置csrf_token到表单中6. 表单的创建
# -*- Coding = utf-8 -*-# @Time : 2020/10/2 16:07# @Author : mdfrom flask import Flask,render_template,request,redirect,flashfrom flask_sqlalchemy import sqlAlchemyfrom flask_wtf.csrf import CSRFProtectapp = Flask(__name__)# 由于使用了flash,所以得设置app.config["SECRET_KEY"] = "wepricsjf"# 使用 CSRFProtect保护appCSRFProtect(app)# 1.设置数据库的配置信息app.config["sqlALCHEMY_DATABASE_URI"] = "MysqL+pyMysqL://root:123456@127.0.0.1:3306/test"app.config["sqlALCHEMY_TRACK_MODIFICATIONS"] = False# 生成对应的SQL语句在控制台# app.config["sqlALCHEMY_ECHO"] = True# 2.创建sqlalchemy对象,关联appdb = sqlAlchemy(app)# 3.编写模型类# 作者(一方)class Author(db.Model):    __tablename__ = "authors"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    # 关系数据,必须有外键为基础    books = db.relationship("Book",backref="author")    # 如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<Author:%s>" % self.name# 书籍(多方)class Book(db.Model):    __tablename__ = "books"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    # 建立外键    author_ID = db.Column(db.Integer,db.ForeignKey(Author.ID))    # 如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<Book:%s,self.name)# 5. 展示数据@app.route('/')def hello_world():    # 1. 查询所有的作者信息,因为作者有外键,可以通过关系数据方便的查出该作者对应的书籍    authors = Author.query.all()    return render_template("library.HTML",authors=authors)# 6. 添加数据@app.route("/add_data",methods=["POST"])def add_data():    # 1. 获取提交的数据    author_name = request.form.get("author")    book_name = request.form.get("book")    # 先判断输入的内容是否为空    if not all([author_name,book_name]):        flash("作者或书籍不能为空")        return redirect("/")    # 2. 根据作者信息查询作者对象    author = Author.query.filter(Author.name == author_name).first()    # 3. 判断作者是否存在    if author:        # 4. 通过书籍名称查询书籍对象,并且这本书的作者的ID和查询出来的作者的ID一样        # 也就是查看要添加的这本书是不是该作者写的        book = Book.query.filter(Book.name == book_name,Book.author_ID == Author.ID).first()        # 5. 判断书籍是否存在        if book:            # return "该作者已经写了这本书了"            flash("已经有该作者写的这本书了")        else:            # 创建书籍对象,添加到数据库            book = Book(name=book_name,author_ID=author.ID)            db.session.add(book)            db.session.commit()    else:        # 作者不存在也是可以添加的        # 先在作者表中进行添加        author = Author(name=author_name)        db.session.add(author)        db.session.commit()        # 然后再书籍表中进行添加        book = Book(name=book_name,author_ID=author.ID)        db.session.add(book)        db.session.commit()    # 6. 重定向到首页    return redirect("/")# 7. 删除书籍@app.route("/delete_book/<int:book_ID>")def delete_book(book_ID):    # 1. 根据ID获取到书籍对象    book = Book.query.get(book_ID)    # 2. 删除这个书籍    db.session.delete(book)    db.session.commit()    # 3. 重定向到页面    return redirect("/")# 7. 删除作者@app.route("/delete_author/<int:author_ID>")def delete_author(author_ID):    # 1. 根据ID获取到作者对象    author = Author.query.get(author_ID)    # 2. 这个作者的全部书籍,由于使用了关系数据,直接这样写就可以    books = author.books    # 3. 遍历这个作者的全部书籍    for book in books:        db.session.delete(book)    # 4. 删除作者    db.session.delete(author)    db.session.commit()    # 5. 重定向到页面    return redirect("/")if __name__ == '__main__':    # 为了演示方便,先删除后创建    db.drop_all()    db.create_all()    # 4. 添加测试数据库    # 生成数据    au1 = Author(name='老王')    au2 = Author(name='老尹')    au3 = Author(name='老刘')    # 把数据提交给用户会话    db.session.add_all([au1,au2,au3])    # 提交会话    db.session.commit()    bk1 = Book(name='老王回忆录',author_ID=au1.ID)    bk2 = Book(name='我读书少,你别骗我',author_ID=au1.ID)    bk3 = Book(name='如何才能让自己更骚',author_ID=au2.ID)    bk4 = Book(name='怎样征服美丽少女',author_ID=au3.ID)    bk5 = Book(name='如何征服英俊少男',author_ID=au3.ID)    # 把数据提交给用户会话    db.session.add_all([bk1,bk2,bk3,bk4,bk5])    # 提交会话    db.session.commit()    app.run()
7. library.HTML
<!DOCTYPE HTML><HTML lang="en"><head>    <Meta charset="UTF-8">    <Title>Title</Title></head><body>{# action: 提交到的地址,method: 表示提交的方式 #}<form action="/add_data" method="post">{# 设置隐藏字段csrf_token,只要使用了CSRFProtect,然后使用模板渲染的时候就可以直接使用csrf_token()方法#}    <input type="hIDden" name="csrf_token" value="{{ csrf_token() }}">    作者: <input type="text" name="author"><br>    书籍: <input type="text" name="book"><br>    <input type="submit" value="添加"><br>    {% for message in get_flashed_messages() %}        <span >{{ message }}</span>    {% endfor %}</form><hr>{# 数据展示 #}<ul>    {# 遍历作者 #}    {% for author in authors %}{#        <li>作者: {{ author.name }}</li>#}{#        <li>作者: {{ author.name }} <a href="/delete_author/{{ author.ID }}">删除</a></li>#}        <li>作者: {{ author.name }} <a href="{{ url_for("delete_author",author_ID=author.ID) }}">删除</a></li>        {# 遍历作者的书籍 #}        <ul>            {% for book in author.books %}{#                <li>书籍: {{ book.name }} </li>#}                <li>书籍: {{ book.name }} <a href="/delete_book/{{ book.ID }}">删除</a></li>            {% endfor %}        </ul>    {% endfor %}</ul></body></HTML>
五、多对多

在项目开发过程中,会遇到很多数据之间多对多关系的情况,比如:

学生网上选课(学生和课程)老师与其授课的班级(老师和班级)用户与其收藏的新闻(用户和新闻)等等...

所以在开发过程中需要使用 ORM 模型将表与表的多对多关联关系使用代码描述出来。多对多关系描述有一个唯一的点就是:需要添加一张单独的表去记录两张表之间的对应关系

1. 需求分析学生可以网上选课,学生有多个,课程也有多个学生有:张三、李四、王五课程有:物理、化学、生物选修关系有:张三选修了化学和生物李四选修了化学王五选修了物理、化学和生物

需求:

@H_736_419@查询某个学生选修了哪些课程查询某个课程都有哪些学生选择

2. 代码
# -*- Coding = utf-8 -*-# @Time : 2020/10/2 19:30# @Author : md'''多对多,学生和课程'''from flask import Flaskfrom flask_sqlalchemy import sqlAlchemyapp = Flask(__name__)# 1.设置数据库的配置信息app.config["sqlALCHEMY_DATABASE_URI"] = "MysqL+pyMysqL://root:123456@127.0.0.1:3306/test"app.config["sqlALCHEMY_TRACK_MODIFICATIONS"] = False# app.config["sqlALCHEMY_ECHO"] = True# 2.创建sqlalchemy对象,关联appdb = sqlAlchemy(app)# 3.编写模型类# 学生class Student(db.Model):    __tablename__ = "students"    ID = db.Column(db.Integer,使用在多对多中的时候注意secondary这个属性,是中间表的表名,用来二次查询    courses = db.relationship("Course",backref="students",secondary="td_student_course")    # 如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<Role:%s>" % self.name# 课程class Course(db.Model):    __tablename__ = "courses"    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    # 如果一个类继承自object那么重写__str__方法即可,如果是继承自db.Model那么需要重写__repr__方法    def __repr__(self):        return "<User:%s>" % self.name# 中间表db.table(    "td_student_course",db.Column("student_ID",db.Integer,db.ForeignKey(Student.ID)),db.Column("course_ID",db.ForeignKey(Course.ID)))@app.route('/')def hello_world():    return "helloworld"# 为了演示,先删除后创建db.drop_all()db.create_all()stu1 = Student(name='张三')stu2 = Student(name='李四')stu3 = Student(name='王五')cou1 = Course(name='物理')cou2 = Course(name='化学')cou3 = Course(name='生物')stu1.courses = [cou2,cou3]stu2.courses = [cou2]stu3.courses = [cou1,cou2,cou3]db.session.add_all([stu1,stu2,stu2])db.session.add_all([cou1,cou3])db.session.commit()if __name__ == '__main__':    app.run()

六、数据库迁移目的: 当数据库的表结构发生变化之后,如果直接删除原有的数据,再添加新的数据,有可能导致数据丢失注意点:1.是为了备份表结构,而不是数据2.如果想要备份数据,需要使用工具,navicat,MysqLworkbench,等等3.更新的过程数据一般不会丢失,做降级的时候需要谨慎 *** 作 *** 作流程:1.安装扩展pip install flask_scriptpip install flask_migrate2.导入三个类from flask_script import Managerfrom flask_migrate import Migrate,MigrateCommand3.通过Manager类创建对象manager,管理appmanager = Manager(app)4.使用Migrate,关联db,appMigrate(app,db)5.给manager添加一条 *** 作命令manager.add_command("db",MigrateCommand)相关迁移命令:生成迁移文件夹[一次就好]python xxx.py db init将模型类生成迁移脚本[重复执行]python xxx.py db migrate -m '注释'将迁移脚本更新到数据库中[重复执行]python xxx.py db upgrade/downgrade [version]其他命令查看最新版本的命令python xxx.py db show查看当前版本python xxx.py db current查看所有的历史版本python xxx.py db history
# -*- Coding = utf-8 -*-# @Time : 2020/10/2 20:38# @Author : mdfrom flask import Flaskfrom flask_script import Managerfrom flask_migrate import Migrate,MigrateCommandfrom flask_sqlalchemy import sqlAlchemyapp = Flask(__name__)# 设置数据库配置信息app.config["sqlALCHEMY_DATABASE_URI"] = "MysqL+pyMysqL://root:123456@localhost:3306/test1"app.config["sqlALCHEMY_TRACK_MODIFICATIONS"] = False# 创建sqlAlchemy对象,关联appdb = sqlAlchemy(app)# 3. 通过Manager类创建对象manager,管理appmanager = Manager(app)# 4.使用Migrate,appMigrate(app,db)# 5.给manager添加一条 *** 作命令manager.add_command("db",MigrateCommand)# 6.编写模型类class Student(db.Model):    ID = db.Column(db.Integer,primary_key=True)    name = db.Column(db.String(32))    age = db.Column(db.Integer)   # email = db.Column(db.String(32))@app.route('/')def hello_world():    return "helloworld"if __name__ == '__main__':    manager.run()

生成迁移文件夹

将模型类生成迁移脚本

将迁移脚本更新到数据库中

此时就可以在数据库中看到对应的表了

此时在代码中多写一行,添加一列

然后继续执行这两条命令,就会看到数据表结构中就多了一列

还可以进行降级

然后再升级到指定的版本

总结

以上是内存溢出为你收集整理的Flask-数据库 *** 作全部内容,希望文章能够帮你解决Flask-数据库 *** 作所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1215953.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-05
下一篇 2022-06-05

发表评论

登录后才能评论

评论列表(0条)

保存