数据模型
# 数据模型 ### 数据库回顾 - 分类: - 关系型数据库:MySQL、sqlite、... - 非关系型数据库:Redis、MongoDB、... - 操作: - 执行原生SQL语句,没都需要拼接SQL语句,而且很容易出错。 - ORM操作,使用ORM可以通过直接操作对象来完成对数据的操作。 ### flask-sqlalchemy - 说明:提供了绝大多数关系型数据库的支持,而且提供了ORM(对象关系映射)。 - 安装:`pip install flask-sqlalchemy` - 连接地址配置: - 名称:`SQLALCHEMY_DATABASE_URI` - 格式: - MySQL:`dialect+driver://username:password@host:port/database` - sqlite:`sqlite:/// + 数据库文件地址` - 使用: ```python from flask_sqlalchemy import SQLAlchemy import os # 当前文件所在目录 base_dir = os.path.abspath(os.path.dirname(__file__)) database_uri = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite') # 配置数据库连接地址 app.config['SQLALCHEMY_DATABASE_URI'] = database_uri # 创建数据库操作对象 db = SQLAlchemy(app) # 设计数据模型 class User(db.Model): # 表名默认是将数据模型类名转换为小写加下划线的风格 # 如:UserModel => user_model # 用于指定表名 __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(20), unique=True) email = db.Column(db.String(50), unique=True) ``` - 管理: ```python @app.route('/create/') def create(): # 创建所有的表 db.create_all() return '数据表已创建' @app.route('/drop/') def drop(): # 删除用户数据表 db.drop_all() return '数据表已删除' # 向终端添加命令创建数据表 @manager.command def createall(): # 先删除原来的数据表 db.drop_all() # 然后再创建 db.create_all() return '数据表已创建' # 向终端添加命令删除数据表 def dropall(): # 删库前给出用户提示信息 if prompt_bool('您确定要删库跑路吗?'): db.drop_all() return '数据表已删除' return '删库有风险,操作需谨慎' ``` > 说明:通过装饰器(@manager.command)修改的函数名就是终端的命令。 > > 使用:python manage.py createall,就可以根据数据模型创建数据表 > > 提示:若创建时数据表已经存在则会失败,可以通过先删除再创建的方式解决,只是副作用有点大。 ### 数据库迁移 - 说明:数据模型的更改应用到数据表中的操作叫数据库迁移。flask-migrate扩展就是专门用来数据库迁移的。 - 安装:`pip install flask-migrate` - 使用: ```python # 导入类库 from flask_migrate import Migrate, MigrateCommand # 创建数据库迁移对象 migrate = Migrate(app, db) # 将数据库迁移命令添加到终端 manager.add_command('db', MigrateCommand) ``` - 迁移: - 初始化,只需要一次,创建一个目录用于存放迁移脚本 ```shell python manage.py db init ``` - 根据模型与数据表,生成迁移脚本 ```shell python manage.py db migrate ``` - 执行迁移脚本 ```shell python manage.py db upgrade ``` - 提示: - 初始化只需要一次,以后生成迁移脚本,然后执行迁移脚本循环操作即可。 - 不是每次迁移都会成功,迁移出错时需要手动解决。 ### 数据的CURD - 增加数据 ```python # 设置自动提交操作,每次请求结束会自动提交操作 app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True # 禁止追踪数据的更改,会销毁额外的性能 app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False @app.route('/insert/') def insert(): # 创建对象 # ming = User(name='ming', email='ming@163.com') # dong = User(name='dong', email='dong@163.com') # 保存单条数据到数据库 # db.session.add(dong) yujie = User(name='yujie', email='yujie@163.com') baoxi = User(name='baoxi', email='baoxi@163.com') long = User(name='long', email='long@163.com') # 保存多条数据到数据库 db.session.add_all([yujie, baoxi, long]) # 提交操作 # db.session.commit() return '数据已添加' ``` - 查询数据 ```python @app.route('/select/<uid>/') def select(uid): # 根据主键查询,查到返回对象,没有查到返回None u = User.query.get(uid) if u: return u.name return '查无此人' ``` - 修改数据 ```python @app.route('/update/<uid>/') def update(uid): u = User.query.get(uid) if u: u.email = 'xxx@163.com' # 下面这句是再次保存,ORM会自动区分更新和插入 db.session.add(u) return '数据已修改' return '查无此人' ``` - 删除数据 ```python @app.route('/delete/<uid>/') def delete(uid): u = User.query.get(uid) if u: # 删除数据 db.session.delete(u) return '数据已删除' return '查无此人' ``` ### 模型设计参考 - 常见字段类型 | 类型名 | python类型 | 说明 | | ------------ | ------------------ | -------------------- | | Integer | int | 整型(32) | | SmallInteger | int | 整型(16) | | BigInteger | int/long | 整型(64) | | Float | float | 浮点数 | | String | str | 变长字符串 | | Text | str | 不受限制的文本 | | Boolean | bool | 布尔值,只有True/False | | Date | datetime.date | 日期 | | Time | datetime.time | 时间 | | Datetime | datetime.datetime | 日期时间 | | Interval | datetime.timedelta | 时间间隔 | | PickleType | pikcle.dumps() | 使用pickle处理后的python对象 | | LargeBinary | bytes | 任意大的二进制数据 | - 常见字段选项 | 选项 | 说明 | | ------------- | ----------------- | | primary_key | 是否作为主键索引,默认为False | | autoincrement | 是否设置字段自增,默认为False | | unique | 是否作为唯一索引,默认为False | | index | 是否作为普通索引,默认为False | | nullable | 字段是否可以为空,默认为True | | default | 设置默认值 | - 总结: - 插入数据可以不传值的字段:自增的主键、有默认值的、可以为空的 - 使用flask-sqlalchemy时要求每个模型都有一个主键,默认名字为id - 模型类名与数据表中的名字 - 默认:将大驼峰格式的模型类名,转换为小写加下划线格式,如:`UserModel => user_model` - 指定:`__tablename__`,使用此类属性指定表名 ### 各种查询 - 说明:绝大多数的数据库操作都是查询,这些操作都是通过方法来体现的。 - 常见操作: | 方法 | 说明 | | ------------ | ------------------------------- | | get | 根据主键进行查询,查到返回对象,没查到返回None | | get_or_404 | 功能同上,查不到时,直接报404错 | | all | 查询所有数据,返回一个列表(元素全是对象) | | first | 返回第一条数据,没有时返回None | | first_or_404 | 功能同上,查不到时报404错 | | limit | 限制结果集数量,返回查询对象,可以继续进行链式查询操作 | | offset | 设置偏移量,返回查询对象,可以继续进行链式查询操作 | | order_by | 结果集排序,可以指定多个字段,asc升序(默认),desc降序 | | count | 统计总数 | - 聚合函数 - 说明:`max、min、sum、avg、count` - 示例: ```python from sqlalchemy import func # 求最大值 max_age = db.session.query(func.max(User.age)).scalar() return str(max_age) ``` - 指定条件查询 ```python # 等值条件查询 users = User.query.filter_by(age=18).all() # 指定任意条件查询 users = User.query.filter(User.age > 20).all() return ','.join(u.name for u in users) ``` ### filter条件查询 - 关系 ```python >, __gt__ 如: users = User.query.filter(User.age > 20).all() # 与上面等价 users = User.query.filter(User.age.__gt__(20)).all() >=, __ge__ <, __lt__ <=, __le__ ==, __eq__ !=, __ne__ ``` - 范围 ```python # users = User.query.filter(User.id.between(1, 3)).all() # users = User.query.filter(User.id.in_((1, 3, 5))).all() users = User.query.filter(User.id.notin_((1, 3, 5))).all() ``` - 内容 ``` startswith:以什么内容开头 endswith:以什么内容结尾 contains:包含什么内容 like:模糊匹配 notlike:模糊匹配相反的条件 ``` - 逻辑 ```python from sqlalchemy import and_, or_ # 默认就是逻辑与 # users = User.query.filter(User.id > 2, User.age > 20).all() # 与上式等价 # users = User.query.filter(and_(User.id > 2, User.age > 20)).all() # 逻辑或 users = User.query.filter(or_(User.id > 2, User.age > 20)).all() ```