数据迁移

安装
    pip install flask-migrate
初始化
    使用app和db进行migrate对象初始化
        from flask_migrate import Migrate
        migrate = Migrate()
        migrate.init_app(app=app, db=db)
    
    安装了flask-script后,可以在manager上添加迁移指令
        from flask_migrate import MigrateCommand
        manager.add_command('db', MigrateCommand)
    
操作
    python manager.py db init  只调用一次, 这里的db是添加命令时给定的名称
    python manager.py db migrate  生成迁移文件
    python manager.py db upgrade  执行迁移中的升级
    python manager.py db downgrade  执行迁移中的降级
  

模型进阶

创建模型

# 模型:类
class Person(db.Model):
    __tablename__ = 'person'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(20), unique=True)
    age = db.Column(db.Integer, default=1)
 
字段类型
Integerint普通整数,一般是 32 位
SmallIntegerint取值范围小的整数,一般是 16 位
BigIntegerint 或 long不限制精度的整数
Floatfloat浮点数
Numericdecimal.Decimal定点数
Stringstr变长字符串
Textstr变长字符串,对较长或不限长度的字符串做了优化
Unicodeunicode变长 Unicode 字符串
UnicodeTextunicode变长 Unicode 字符串,对较长或不限长度的字符串做了优化
Booleanbool布尔值
Datedatetime.date日期
Timedatetime.time时间
DateTimedatetime.datetime日期和时间
Intervaldatetime.timedelta时间间隔
LargeBinarystr二进制文件
常用约束
primary_key如果设为 True ,这列就是表的主键
unique如果设为 True ,这列不允许出现重复的值
index如果设为 True ,为这列创建索引,提升查询效率
nullable如果设为 True ,这列允许使用空值;如果设为 False ,这列不允许使用空值
default为这列定义默认值

模型操作

单表操作

增加数据
    a. 一次增加一条数据:
        p = Person()    
        p.name = '小明'    
        p.age = 22
        try:
            db.session.add(p)
            db.session.commit()
        except:
            # 回滚
            db.session.rollback()
            db.session.flush()
    b. 一次添加多条数据
        persons = []
        for i in range(10,30):
            p = Person()
            p.name = '宝强' + str(i)
            p.age = i
            persons.append(p)
        db.session.add_all(persons)
        db.session.commit()
删除数据
    p = Person.query.first()  # 获取第一条数据
    db.session.delete(p)
    db.session.commit()
    
修改数据
    p = Person.query.first()
    p.age = 100
    db.session.commit()
        
查询数据
    过滤器
        filter()    把过滤器添加到原查询上,返回一个新查询
        filter_by() 把等值过滤器添加到原查询上,返回一个新查询
        limit()   使用指定的值限制原查询返回的结果数量,返回一个新查询
        offset()    偏移原查询返回的结果,返回一个新查询
        order_by()  根据指定条件对原查询结果进行排序,返回一个新查询
        group_by()  根据指定条件对原查询结果进行分组,返回一个新查询
    常用查询
        all()   以列表形式返回查询的所有结果
        first() 返回查询的第一个结果,如果没有结果,则返回 None
        first_or_404()  返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应
        get()   返回指定主键对应的行,如果没有对应的行,则返回 None
        get_or_404()    返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错误响应
        count() 返回查询结果的数量
        paginate()  返回一个 Paginate 对象,它包含指定范围内的结果
        查询属性
            contains
            startswith
            endswith
            in_
            __gt__
            __ge__
            __lt__
            __le__
    逻辑运算
        与 and_
            filter(and_(条件),条件…)
        或 or_
            filter(or_(条件),条件…)
        非 not_
            filter(not_(条件),条件…)
    示例:   
        查询:
            persons = Person.query.all()  # 获取所有
            persons = Person.query.filter(Person.age>22)
            # filter功能比filter_by强大
            persons = Person.query.filter(Person.age==22)  # filter(类.属性==值)
            persons = Person.query.filter_by(age=22) # filter_by(属性=值)
            persons = Person.query.filter(Person.age.__lt__(22)) # <
            persons = Person.query.filter(Person.age.__le__(22)) # <=
            persons = Person.query.filter(Person.age.__gt__(22)) # >
            persons = Person.query.filter(Person.age.__ge__(22)) # >=
            persons = Person.query.filter(Person.age.startswith('宝'))  # 开头匹配
            persons = Person.query.filter(Person.age.endswith('宝'))  # 结尾匹配
            persons = Person.query.filter(Person.age.contains('宝'))  # 包含
            persons = Person.query.filter(Person.age.in_([11,12,22]))  # in_
            persons = Person.query.filter(Person.age>=20, Person.age<30)  # and_
            persons = Person.query.filter(and_(Person.age>=20, Person.age<30))  # and_
            persons = Person.query.filter(or_(Person.age>=30, Person.age<20))  # or_
            persons = Person.query.filter(not_(Person.age<30))  # not_
        
        排序:
            persons = Person.query.limit(5)  # 取前5个
            persons = Person.query.order_by('age')  # 升序
            persons = Person.query.order_by('-age')  # 降序
            persons = Person.query.order_by(desc('age'))  # 降序
            persons = Person.query.offset(5)  # 跳过前5个
        
        分页:
            # 获取页码page和每页数量num
            page = int(request.args.get('page'))
            num = int(request.args.get('num'))
            # 手动做分页
            persons = Person.query.offset((page-1) * num).limit(num)
            # 使用paginate做分页
            persons = Person.query.paginate(page, num, False).items
       
        paginate对象的属性:
            items:返回当前页的内容列表
            has_next:是否还有下一页
            has_prev:是否还有上一页
            next(error_out=False):返回下一页的Pagination对象
            prev(error_out=False):返回上一页的Pagination对象
            page:当前页的页码(从1开始)
            pages:总页数
            per_page:每页显示的数量
            prev_num:上一页页码数
            next_num:下一页页码数
            query:返回创建该Pagination对象的查询对象
            total:查询返回的记录总数
            
 

多表关联

一对多
# 一对多
class Grade(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(16))
    # 定义班级标的一对多关系,不是字段, Student为学生表模型, backref为反向查找名称
    students = db.relationship('Student', backref='grade1', lazy=True)
class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(16))
    age = db.Column(db.Integer, default=1)
    # 创建外键,关联到班级表的主键,实现一对多关系,班级表中也要有对应操作
    grade = db.Column(db.Integer, db.ForeignKey(Grade.id))
    查: 
        # 获取学生的所在班级信息(反向)
        stu = Student.query.get(stuid)
        grade = stu.stus
        
        # 获取班级的所有学生 (正向)
        grade = Grade.query.get(gradeid)
        students = grade.students
    删:
            # 删除班级后, 学生的grade字段会变为null
            grade = Grade.query.get(id)
            db.session.delete(grade)
            db.session.commit()
 
多对多
用户收藏电影,一个用户可以收藏多部电影, 一部电影可以被不同的用户收藏, 是一个多对多关系.
# 中间表(不是模型)
collects = db.Table('collects',
    # user_id为表字段名称, user.id为外键表的id
    db.Column('user_id', db.Integer, db.ForeignKey('user.id'), primary_key=True),
    db.Column('movie_id', db.Integer, db.ForeignKey('movie.id'), primary_key=True)
)
class Movie(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(200))
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(16))
    age = db.Column(db.Integer, default=1)
    # 多对多  关联的学生表格的模型, 中间表的名称, 反向查找
    movies = db.relationship('Movie',  backref='users', secondary=collects, lazy='dynamic')
    
    lazy属性:
        懒加载,可以延迟在使用关联属性的时候才建立关联
        lazy='dynamic': 会返回一个query对象(查询集),可以继续使用其他查询方法,如all().
        lazy='select': 首次访问到属性的时候,就会全部加载该属性的数据.
        lazy='joined': 在对关联的两个表进行join操作,从而获取到所有相关的对象
        lazy=True:  返回一个可用的列表对象,同select
        
查:
    # 查询用户收藏的所有电影
     user = User.query.get(id)
     movies = user.movies
        
    # 查询电影被哪些用户收藏
     movie = Movie.query.get(id)
     users = movie.users
    
删:
    # 中间表的数据会被级联删除
     movie = Movie.query.get(id)
     db.session.delete(movie)
     db.session.commit()
     
增:
    # 用户收藏电影
    user = User.query.get(id)
    movie = Movie.query.get(id)
    user.movies.append(movie)
    db.session.commit()
 
05-02 04:52