Web后端学习笔记 Flask (5) 数据库

MySql数据库安装:省略

SQLAlchemy介绍和基本使用:

数据库是开发网站的基础,,在Flask中,支持的数据库有:MySql,PostgreSql,SQLite,Redis,MongoDB来写原生的语言实现功能,也可以使用高级别的数据库抽象方式,例如SQLAlchemy或者MongoEngine这样的ORM.

除此之外,还需要在python中安装pymsql,以及SQLAlchemy

SQLAlchemy可以独立于Flask使用,对数据库进行操作,可以避免写sql语句。

连接的格式:dialect+driver://username:password@host:port/database

SQLAlchemy连接数据库实例代码:

# -*- coding: utf-8 -*-
from sqlalchemy import create_engine

HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)

conn = engine.connect()

result = conn.execute("select 1")
print(result.fetchone())

数据库ORM介绍:

使用SQLAlchemy可以写原生的sql语句,也可以使用数据库ORM对数据库进行操作,ORM(Object Relation MAP),及对象关系映射,即数据库表与模型对象之间的映射。将数据库中的表与python中的类映射起来。

这样主要是为了解决:

1. SQL语句重复利用率不高,且越复杂的sql语句条件越多,代码越长,会出现很多相近的SQL语句。

2. 很多SQL语句是在业务逻辑中拼出来的,如果有数据库需要更改,就要去修改大量的sql语句,容易造成错误及遗漏

3. 写sql时容易忽略web安全问题,给未来造成隐患。

ORM优点:

1. 易用性:使用ORM做数据库开发,可以有效减少重复SQL语句,写出来的模型更加的直观,清晰

2. 性能损耗小:ORM转换成底层数据库指令会有一些开销,但是通过不断的优化,这种性能开销已经非常小,且综合考虑易用性,这种损耗是值得的。(数据:性能损耗不足5%)

3. 设计灵活:可以轻松写出复杂的查询

4. 可移植性:SQLAlchemy封装了底层的数据库实现,支持多个关系数据库引擎,可以非常容易的切换数据库。

映射的步骤:

1. 用declarative_base根据engine创建一个ORM基类Base

2. 用Base作为基类创建自定义的ORM类,定义__tablename__属性来指定创建好的表名,

3. 创建属性来映射到表中的字段,所有的属性都应该为Column类型

4. 使用Base.metadata.create_all()来将模型映射到数据库中

# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类


# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中

class Person(Base):
    __tablename__ = "person"     # 定义表的名字
    id = Column(Integer, primary_key=True, autoincrement=True)      # 创建字段
    name = Column(String(50))
    age = Column(Integer)


Base.metadata.create_all()

运行脚本,在cms数据库中创建表person:

将模型映射到数据库之后,即使改变了类中的属性重新运行代码,也不会重新映射模型。

SQLALchemy属性常用数据类型:

1. Column常用参数

default:默认值

nullable:是否可空

primary_key:是否为主键

unique:是否唯一

autoincrement:是否自动增长

name:该属性在数据库中的映射字段

onupdate: 如果数据库中某个字段被更新了,则onupdate=True的字段的值也会随着更新。(例如,一篇文章的更新时间的属性可以设置位onupdate=True, 当文章的标题被修改后,对应的更新时间也会发生变化)

2. 常用数据类型

Integer:整形,映射到数据库中是int类型

Float:浮点类型,32位,会出现精度丢失

Double:浮点类型,64位,会出现精度丢失

Boolean:传递True/False进去

DECIMAL:定点类型,不会出现精度丢失,有参数,DECIMAL(10,4), 存储10位长度的数字,精确到小数点后4位

enum:枚举类型, Enum('a', 'b', 'c')

Date:传递datetime.date()参数    (年月日)

DateTime:传递datetime.datetime()进去      (年月日时间)

Time:传递datetime.time()进去          (只有时间)

String:字符类型,使用时需要指定长度,区别于Text类型

Text:文本类型

LONGTEXT:长文本类型

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    view_cnt = Column(Integer, default=0)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, default=datetime.now)   # 创建时间


Base.metadata.drop_all()
Base.metadata.create_all()


artice = Article(title="Gone With Wind", view_cnt=11)
db_session.add(artice)
db_session.commit()      # 提交

可以查询数据库中的数据:

还可以使用一个方法:

artice1 = Article(title="Gone With Wind", view_cnt=11)
artice2 = Article(title="Gone With Wind", view_cnt=11)
db_session.add_all([artice1, artice2])
db_session.commit()      # 提交
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    view_cnt = Column(Integer, default=0)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, default=datetime.now)   # 创建时间
    update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now)
    # create_time必须同时指定default值和onupdate值,在第一次插入数据的时候会有时间,
    # 在修改数据的时候也会更新时间


Base.metadata.drop_all()
Base.metadata.create_all()


artice1 = Article(title="Gone With Wind", view_cnt=11)
artice2 = Article(title="Jane Austing")
db_session.add_all([artice1, artice2])
db_session.commit()      # 提交


# article = db_session.query(Article).first()
# article.title = "Jane Aye"
# db_session.commit()   # 提交


数据展示:

现在对数据进行修改:

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    view_cnt = Column(Integer, default=0)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, default=datetime.now)   # 创建时间
    update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now)
    # create_time必须同时指定default值和onupdate值,在第一次插入数据的时候会有时间,
    # 在修改数据的时候也会更新时间


# Base.metadata.drop_all()
# Base.metadata.create_all()
# 
# 
# artice1 = Article(title="Gone With Wind", view_cnt=11)
# artice2 = Article(title="Jane Austing")
# db_session.add_all([artice1, artice2])
# db_session.commit()      # 提交


article = db_session.query(Article).first()   # 查询数据
article.title = "Jane Aye"                    # 修改数据
db_session.commit()   # 提交

数据展示:

可以看到,被修改的数据,title字段发生了变化,同时,update_time字段的值也随着更新了,但是update_time字段必须指定默认值,在第一次插入数据的时候使用。

query函数可查询的数据:

1. query可用参数:

/模型对象,指定查找这个模型中的所有的对象

/模型中的属性,可以指定查找某个模型的其中几个属性

/聚合函数,func.count, func.avg, func.max, func.min,func.sum

首先,在数据库中插入一些测试数据

# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import random


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    price = Column(Float, nullable=False)

    def __repr__(self):
        return "<article>" + str(self.id) + " | " + self.title + " | " + str(self.price)


# Base.metadata.drop_all()
# Base.metadata.create_all()

# 数据库中插入测试数据
# for cnt in range(10):
#     article = Article(title="article" + str(cnt), price=random.randint(25, 100))
#     db_session.add(article)
# db_session.commit()      # 提交


# 查询数据
articles = db_session.query(Article).all()   # 查询数据   # 返回article对象
for article in articles:
    print(article)

print("-"*50)
articles_title_price = db_session.query(Article.title, Article.price).all()   # 返回元组
for data in articles_title_price:
    print(data)


print("-"*50)
# 聚合函数
count = db_session.query(func.count(Article.id)).all()
print("共有{}条数据".format(count[0]))

average_price = db_session.query(func.avg(Article.price)).all()
print("平均值{}".format(average_price[0]))

filter函数实现条件过滤:

过滤是数据提取中的一个重要的功能,常用的过滤功能有:

1. equals

2. not equals

3. like    做匹配   ilike 不区分大小写

4. in

5. not in

6. is null

7. is not null

8. and

9. or

示例代码:

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func
from sqlalchemy import and_, or_
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import random


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    price = Column(Float, nullable=False)

    def __repr__(self):
        return "<article>" + str(self.id) + " | " + self.title + " | " + str(self.price)


# Base.metadata.drop_all()
# Base.metadata.create_all()

# 数据库中插入测试数据
# for cnt in range(10):
#     article = Article(title="article" + str(cnt), price=random.randint(25, 100))
#     db_session.add(article)
# db_session.commit()      # 提交


# equal
articles = db_session.query(Article).filter(Article.id == 1).all()   # 查询数据   # 返回article对象
for article in articles:
    print(article)


# not equal
print("-"*50)
articles = db_session.query(Article).filter(Article.title != "title2").all()   # 查询数据   # 返回article对象
for article in articles:
    print(article)


# like
print("-"*50)
articles_title_price = db_session.query(Article).filter(Article.title.like("shel%")).all()   # 返回元组
for data in articles_title_price:
    print(data)


# in_
print("-"*50)
articles = db_session.query(Article).filter(Article.title.in_(["shell", "article3", "article1"])).all()
for article in articles:
    print(article)

# not in
print("-"*50)
articles = db_session.query(Article).filter(~Article.title.in_(["shell", "article3", "article1"])).all()
for article in articles:
    print(article)

# is null
print("-"*50)
articles = db_session.query(Article).filter(Article.title is None).all()
for article in articles:
    print(article)


# is not null
print("-"*50)
articles = db_session.query(Article).filter(Article.title is not None).all()
for article in articles:
    print(article)

# and 用于多个条件连接   and_也可以用
print("-"*50)
articles = db_session.query(Article).filter(Article.title == "article8",
                                            Article.id == 9).all()
for article in articles:
    print(article)


# or 用于多个条件连接
print("-"*50)
articles = db_session.query(Article).filter(or_(Article.title == "article8",
                                            Article.id == 2)).all()
for article in articles:
    print(article)


如果想查看原生的SQL语句,可以不在后面加all()或者first()

外键及其四种约束

1. 外键:是多表之间进行关联的基础,SQLAlchemy同样也支持外键,同构foreignKey来实现,并且可以指定表的外键约束。

# -*- coding: utf-8 -*-

from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func
from sqlalchemy import Text, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import random


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)
    uid = Column(Integer, ForeignKey("user.id"), nullable=False)  # 创建外键

    def __repr__(self):
        return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid)


class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)


Base.metadata.drop_all()
Base.metadata.create_all()

使用SQLAlchemy创建外键,需要在从表中增加一个字段,指定这个字段外键的是哪个表的哪个字段即可,从表中外键的字段,必须和父表的主键字段类型保持一致。

外键约束: 由于从表会引用主表中的字段,从表中的总段记录删除,主表中的数据应该如何变化呢?

外键约束有以下几项:
RESTRICT:父表数据被删除,会阻止删除

NO ACTION:在mysql中,同RESTRICT

CASCADE:级联删除,父表中的数据删除,子表中的数据也跟随删除

SET NULL:父表数据被删除,子表数据相应的字段会被设置位null

class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)
    uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"), nullable=False)  # 创建外键
    # ondelete表示的是外键的约束

    def __repr__(self):
        return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid)

ORM层面外键的优化使用,以及一对多的关系

在两个表中查询数据:

# -*- coding: utf-8 -*-

from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func
from sqlalchemy import Text, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import random


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)
    uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"), nullable=False)  # 创建外键
    # ondelete表示的是外键的约束

    def __repr__(self):
        return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid)


class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

    def __repr__(self):
        return str(self.id) + " | " + self.username


article = db_session.query(Article).first()
uid = article.uid
user = db_session.query(User).filter(User.id == uid).first()
print(article.title, article.content, user.username)

上述查找数据的方法过于繁琐,sqlAlchemy提供了更加简便和强大的方法:

一对多与多对一:

SQLAlchemy提供的relationship可以定义属性,在以后访问相关联的表的时候,就可以直接通过属性访问的方式进行访问

# -*- coding: utf-8 -*-

from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, func
from sqlalchemy import Text, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from datetime import datetime
import random


HOST_NAME = "127.0.0.1"
PORT = "3306"
DATABASE = "cms"
USERNAME = "root"
PASSWORD = "root1234"

# dialect+driver://username:password@host:port/database

DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{database}".format(
    username=USERNAME, password=PASSWORD, host=HOST_NAME, port=PORT, database=DATABASE
)

# 创建数据库引擎
engine = create_engine(DB_URI)
Base = declarative_base(engine)   # 通过继承Base创建ORM模型/ 创建基类

db_session = sessionmaker(engine)()   # 创建会话,才能实现增删改查
# 1. 创建ORM模型,必须继承自SQLAlchemy:
# 2. 在ORM中创建一些属性,跟表中的字段一一映射,这些属性必须是SQLAlchemy提供的数据类型
# 3. 将创建好的ORM模型映射到数据库中


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)
    uid = Column(Integer, ForeignKey("user.id", ondelete="RESTRICT"), nullable=False)  # 创建外键
    # ondelete表示的是外键的约束

    author = relationship("User", backref="articles")    # relationship 一个Article对应一个author

    def __repr__(self):
        return "<article>" + str(self.id) + " | " + self.title + " | " + " | " + str(self.uid)


class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

    # articles也可以通过在Article中赋值backref参数实现
    # articles = relationship("Article")  # relationship 一个author可能对应多个Article

    def __repr__(self):
        return str(self.id) + " | " + self.username


article = db_session.query(Article).first()
print(article.author.username)

print("-"*50)
user = db_session.query(User).filter(User.id == article.uid).first()
print(user.articles)

----------------------------------------------------------------------------------------------------------------

posted @ 2020-04-09 12:19  Alpha205  阅读(107)  评论(0编辑  收藏  举报