falsk_sqlalchemy高级

 

 关联查询

  • 基本查询
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 配置数据库的连接地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/test'

# 配置是否监听数据库变化, 性能较差, 不属于sqlalchemy本体
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 一旦开启, 可以显示底层执行的SQL语句
app.config['SQLALCHEMY_ECHO'] = True

# 创建数据库连接
db = SQLAlchemy(app)

​

# 用户表  一
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True)


# 地址表  多
class Address(db.Model):
    __tablename__ = 't_address'
    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String(20))
    user_id = db.Column(db.Integer)  #  逻辑外键


@app.route('/')
def index():
    # 添加数据
    user1 = User(name='zs')
    db.session.add(user1)
    db.session.flush()  # 主动发送sql
    # 关联数据
    adr1 = Address(detail='上海中心', user_id=user1.id)
    adr2 = Address(detail='陆家嘴1号', user_id=user1.id)
    db.session.add_all([adr1, adr2])
    db.session.commit()
     
    # 关联查询
    user1 = User.query.filter(User.name=='zs').first()
    adrs = Address.query.filter(Address.user_id==user1.id).all()
    for adr in adrs:
        print(adr.detail)
    
    return 'index'

if __name__ == '__main__'
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 关系属性(代码中配置部分这里偷懒不写)
# 用户表  一
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True)
    # 2. 定义关系属性  relationship('关联的表对应的类名')
    addresses = db.relationship('Address')

# 地址表  多
class Address(db.Model)
    __tablename__ = 't_address'
    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String(20)
    # 1. 设置外键参数
    user_id = db.Column(db.Integer, db.ForeignKey('t_user')  # 逻辑外键, 测试用
   
@app.route('/')
def index():
    # 添加数据
    user1 = User(name='zs')
    db.session.add(user1)
    db.session.flush()  # 主动发sql
    # 关联数据
    adr1 = Address(detail='上海中心', user_id=user1.id)
    adr2 = Address(detail='陆家嘴1号', user_id=user.id)
    db.session.add_all([adr1, adr2])
    db.session.commit()

    # 使用关系属性来关联查询 1> 定义外键参数 2> 定义关系属性  本质还是通过外键查询
    user1 = User.query.filter(User.name == 'zs').first()
    # adrs = Address.query.filter(Address.user_id==user1.id).all()
    for adr in user1.addresses:
        print(adr.detail)

    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 反向引用
# 用户表 一
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True)
    # 关系属性  可以设置backref='反向属性名' 来代替反向关系属性
    addresses = db.relationship('Address', backref='user')


# 地址表 多
class Address(db.Model):
    __tablename__ = 't_address'
    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String(20))
    user_id = db.Column(db.Integer, db.ForeignKey('t_user.id'))  # 逻辑外键
    # user = db.relationship('User')  # 反向关系属性


@app.route('/')
def index():
    # 添加数据
    user1 = User(name='zs')
    db.session.add(user1)
    db.session.flush()
    # 关联数据
    adr1 = Address(detail='中关村1号', user_id=user1.id)
    adr2 = Address(detail='陆家嘴1号', user_id=user1.id)
    db.session.add_all([adr1, adr2])
    db.session.commit()

    # 使用关系属性来关联查询
    # user1= User.query.filter(User.name=='zs').first()

    # for adr in user1.addresses:
    #     print(adr.detail)

    # 反向查询
    print(adr1.user.name)
    # print(user1)
    # print(user1.addresses)
    # print(type(user1))
    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 动态查询
# 用户表 一
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True)
    # 关系属性  设置lazy='dynamic'开启动态查询, 关系属性不再返回列表, 而是返回AppenderBaseQuery对象
    # AppenderBaseQuery对象: 1> 类似BaseQuery, 可以续接查询条件  2> 保留了列表的特性 支持遍历和索引取值
    addresses = db.relationship('Address', backref='user', lazy='dynamic')


# 地址表 多
class Address(db.Model):
    __tablename__ = 't_address'
    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String(20))
    user_id = db.Column(db.Integer, db.ForeignKey('t_user.id'))  # 逻辑外键


@app.route('/')
def index():
    # 添加数据
    user1 = User(name='zs')
    db.session.add(user1)
    db.session.flush()
    # 关联数据
    adr1 = Address(detail='中关村1号', user_id=user1.id)
    adr2 = Address(detail='陆家嘴1号', user_id=user1.id)
    db.session.add_all([adr1, adr2])
    db.session.commit()

    # 使用关系属性来关联查询
    user1 = User.query.filter(User.name == 'zs').first()
    # ret = user1.addresses.filter(Address.detail.startswith('中关村')).all()

    # 通过遍历索引取值  __iter__
    # for address in user1.addresses:
    #     print(address)
    adr = user1.addresses[0]
    print(adr.detail)

    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 优化关联查询
from sqlalchemy.orm import Load


# 用户表 一
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), unique=True)
    # 关系属性  设置lazy='dynamic'开启动态查询, 关系属性不再返回列表, 而是返回AppenderBaseQuery对象
    # AppenderBaseQuery对象: 1> 类似BaseQuery, 可以续接查询条件  2> 保留了列表的特性 支持遍历和索引取值
    addresses = db.relationship('Address', backref='user', lazy='dynamic')


# 地址表 多
class Address(db.Model):
    __tablename__ = 't_address'
    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String(20))
    user_id = db.Column(db.Integer, db.ForeignKey('t_user.id'))  # 逻辑外键


@app.route('/')
def index():
    # 添加数据
    user1 = User(name='zs')
    db.session.add(user1)
    db.session.flush()
    # 关联查询
    adr1 = Address(detail='中关村1号', user_id=user1.id)
    adr2 = Address(detail='陆家嘴1号', user_id=user1.id)
    db.session.add_all([adr1, adr2])
    db.session.commit()

    # sqlalchemy默认就采用懒查询机制, 不使用关系属性, 不会主动查询关联数据, 优点: 减少不必要的查询, 优化性能
    # user1 = User.query.filter(User.name == 'zs').first()
    # 使用关系属性时, 才查询关联数据 (一共查询了两次), 第一次查询用户数据, 第二次是查用户关联的地址数据
    # print(user1.addresses)

    # 需求: 直接查询出关联数据 join关联查询 select t_user.name, t_adr.detail from t_user join t_adr on t_adr.user_id=t_user.id where t_user.name='zs'
    # 一次join的性能一般会比分成两次查询的效率高, 网络IO会影响性能
    # ret = db.session.query(User, Address).join(Address, Address.user_id == User.id).filter(User.name == 'zs').all()

    # 只查询用户和地址的指定字段
    ret = db.session.query(User, Address).options(Load(User).load_only(User.id), Load(Address).load_only(Address.detail)).join(Address, Address.user_id==User.id).filter(User.name=='zs').all()
    print(ret)
    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 一对一关系
# 用户表 一
class UserAccount(db.Model):
    __tablename__ = 't_useraccount'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=True)
    # 关系属性  关系属性默认是基于一对多关系生成的, 返回的是列表
    # 如果是对一关系, 可以设置uselist=False, 则直接返回模型对象
    info = db.relationship('UserInfo', uselist=False)


# 用户信息表  一
class UserInfo(db.Model):
    __tablename__ = 't_userinfo'
    id = db.Column(db.Integer, primary_key=True)
    age = db.Column(db.Integer)
    height = db.Column(db.Float)
    user_id = db.Column(db.Integer, db.ForeignKey('t_useraccount.id'))  # 外键


@app.route('/')
def index():
    user1 = UserAccount(name='zs')
    db.session.add(user1)
    db.session.flush()
    # 关联数据
    info1 = UserInfo(age=20, height=1.9, user_id=user1.id)
    db.session.add(info1)
    db.session.commit()

    # 关联查询
    user_obj = UserAccount.query.filter(UserAccount.name == 'zs')
    info = user1.info
    print(info)  # 关系属性中添加uselist=False后, 返回的是对象
    print(info.age, info.height)

    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 同表的多个关系
# 用户表  用户表和地址表有两个关联, 一个用户有一个家庭地址, 还有一个工作地址
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(40), unique=True)
    work_adr_id = db.Column(db.Integer, db.ForeignKey('t_address.id'))
    home_adr_id = db.Column(db.Integer, db.ForeignKey('t_address.id'))

    # 关系属性  两个表如果有多个关系, 需要通过primaryjoin来进行区分
    work_adr = db.relationship('Address', primaryjoin='User.work_adr_id==Address.id')
    home_adr = db.relationship('Address', primaryjoin='User.home_adr_id==Address.id')


# 地址表
class Address(db.Model):
    __tablename__ = 't_address'
    id = db.Column(db.Integer, primary_key=True)  # 主键
    detail = db.Column(db.String(40), unique=True)


@app.route('/')
def index():
    adr1 = Address(detail='陆家嘴1号院')
    adr2 = Address(detail='万科翡翠滨江')
    db.session.add_all([adr1, adr2])
    db.session.flush()
    # 关联数据
    user1 = User(name='zs', work_adr_id=adr1.id, home_adr_id=adr2.id)
    db.session.add(user1)
    db.session.commit()

    # 关联查询
    print(user1.work_adr.detail, user1.home_adr.detail)

    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 多对多关系
# 定义关系表, 记录关联
class StudentCourse(db.Model):
    __tablename__ = 't_stu_cur'
    id = db.Column(db.Integer, primary_key=True)
    stu_id = db.Column(db.Integer, db.ForeignKey('t_student.id'))  # 记录学生的主键
    cur_id = db.Column(db.Integer, db.ForeignKey('t_course.id'))  # 记录课程的主键


# 学生表 多
class Student(db.Model):
    __tablename__ = 't_student'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=True)
    # 定义关系属性, 多对多关系属性也需要设置primarilyjoin来指定关联的字段
    courses = db.relationship('StudentCourse', primaryjoin='StudentCourse.stu_id==Student.id')


# 课程表  多
class Course(db.Model):
    __tablename__ = 't_course'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=True)


@app.route('/')
def index():
    stu1 = Student(name='zs')
    cur1 = Course(name='python')
    cur2 = Course(name='java')
    db.session.add_all([stu1, cur1, cur2])
    db.session.flush()
    # 关联数据
    sc1 = StudentCourse(stu_id=stu1.id, cur_id=cur1.id)
    sc2 = StudentCourse(stu_id=stu1.id, cur_id=cur2.id)
    db.session.add_all([sc1, sc2])
    db.session.commit()

    # 关联查询
    # print(stu1.courses)
    # 一次join(优化)
    ret = db.session.query(StudentCourse, Student, Course).join(Student, Student.id == StudentCourse.stu_id).join(
        Course, Course.id == StudentCourse.cur_id).filter(Student.name == 'zs').all()
    print(ret)

    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 自关联一对多关系
# 评论表  一条评论可以有多条子评论
class Comment(db.Model):
    __tablename__ = 'info_comment'
    id = db.Column(db.Integer, primary_key=True)  # 评论编号
    content = db.Column(db.Text, nullable=False)  # 评论内容
    parent_id = db.Column(db.Integer, db.ForeignKey('info_comment.id'))  # 父评论id
    # 定义关系属性
    # 为了区分自关联一对多关系属性, 需要给多对一的关系属性设置remote_side=[主键]参数
    children = db.relationship('Comment')  # 取所有的子评论, 一对多关系
    parent = db.relationship('Comment', remote_side=[id])  # 取父评论  多对一关系

    # 反向引用版
    # children = db.relationship('Comment', db.backref('parent', remote_side=[id]))


@app.route('/')
def index():
    comment1 = Comment()
    comment1.content = '哈哈'
    comment2 = Comment()
    comment2.content = '子评论'
    # 关联数据
    comment2.parent = comment1
    db.session.add_all([comment1, comment2])
    db.session.commit()

    # 关联查询
    # print(comment1.children)  # 获取所有的子评论
    # print(comment2.parent)  # 获取父评论
    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')
  • 事务
# 用户表
class User(db.Model):
    __tablename__ = 't_user'
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(20), unique=True)
    age = db.Column(db.Integer)


@app.route('/')
def index():
    """
    1. sqlalchemy会自动创建隐式事务, 将sql操作添加到事务中
    2. 事务提交失败, 会自动回滚
    3. 也可以手动回滚, 也可以设置mysql的锁
    """
    # 需求: 当数据量<10时, 才添加数据, 否则回滚
    # 添加数据
    user1 = User(name='zs', age=20)
    user2 = User(name='ls', age=20)
    user3 = User(name='ws', age=20)
    user4 = User(name='ys', age=20)
    db.session.add_all([user1, user2, user3, user4])
    """
    会先插入数据, 然后查询, 将查询结果进行比较,如果不符合条件则会滚, 符合则commit
    """
    # with_for_update()  设置排它锁  with_for_update(read=True) 设置共享锁
    if User.query.with_for_update().count() < 3:

        ret = User.query.count()
        print(ret)

        db.session.commit()
    else:

        db.session.rollback()  # 不满足条件, 主动回滚

    return 'index'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    app.run(debug=True, host='0.0.0.0')

猜你喜欢

转载自blog.csdn.net/Regan_Yu/article/details/95173548
今日推荐