关联查询
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')