1.定义
“多对多”是指关系型数据库中的模型对象的多对多的模型关系,它表示一个模型(即表)的多条记录(即行)对应多个另一个模型(即另一个表)的记录。简单来说,一个模型中的多个记录关联另一个模型中的多个记录。比如多个用户对象可以收藏多部电影对象。
以用户对象以及电影对象为例写个代码
from enum import Enum
class GenderEnum(Enum):
MALE = '男'
FEMALE = '女'
# 中间表:收藏表
collect = db.Table(
'collects',
db.Column('user_id', db.Integer, db.ForeignKey('tb_movie_users.id'), primary_key=True),
db.Column('movie_id', db.Integer, db.ForeignKey('tb_movie.id'), primary_key=True),
)
# 电影用户模型
class MovieUserModel(db.Model):
__tablename__ = "tb_movie_users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(30), unique=True, index=True)
age = db.Column(db.Integer, default=1)
gender = db.Column(db.Enum(GenderEnum))
# 电影模型
class MovieModel(db.Model):
__tablename__ = "tb_movie"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
movie_name = db.Column(db.String(30), unique=True, index=True)
# 关联
movie_users = db.relationship('MovieUserModel', backref='movies', lazy='dynamic', secondary=collect)
特别说明下,这种多对多模型设置的中间表关联外键时候,如果用 __tablename__指定了表名,那就需要用这个表名,而不是使用小写的模型对象名,不然会引起sqlalchemy.exc.NoReferencedTableError错误,比如“sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'collects.user_id' could not find table 'movieusermodel' with which to generate a foreign key to target column 'id'”
2.数据迁移
(1)flask db migrate创建迁移文件(60b44f7a29f9_.py)
(flask_env) PS D:\learn\using_the_flask\模型基础> flask db migrate
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'tb_movie'
INFO [alembic.autogenerate.compare] Detected added index 'ix_tb_movie_movie_name' on '['movie_name']'
INFO [alembic.autogenerate.compare] Detected added table 'tb_movie_users'
INFO [alembic.autogenerate.compare] Detected added index 'ix_tb_movie_users_username' on '['username']'
INFO [alembic.autogenerate.compare] Detected added table 'collects'
Generating D:\learn\using_the_flask\模型基础\migrations\versions\60b44f7a29f9_.py ... done
(2)flask db upgrade 执行迁移文件中的upgrade升级,创建数据表
(flask_env) PS D:\learn\using_the_flask\模型基础> flask db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade e7a1718370d7 -> 60b44f7a29f9, empty message
3.数据操作
(1)增
添加用户
@user_bp.route("/addmu")
def add_movie_user():
# 添加电影用户
users_list = []
for i in range(10):
# 用户名
m_user = MovieUserModel()
m_user.username = f'Shirley{i}'
m_user.gender = GenderEnum.FEMALE
users_list.append(m_user)
try:
# 添加电影用户列表
db.session.add_all(users_list)
db.session.commit()
return "添加电影用户成功!"
except:
db.session.rollback() # 回滚
添加电影
@user_bp.route("/addmv")
def add_movie():
# 添加电影
movies_list = []
for i in range(1, 10):
# 电影名
m_name = MovieModel()
m_name.movie_name = f'速度与激情{i}'
movies_list.append(m_name)
try:
# 添加电影用户列表
db.session.add_all(movies_list)
db.session.commit()
return "添加电影成功!"
except:
db.session.rollback() # 回滚
接下来重点讲下多对多操作的多表交互的添加,这个是主要内容,在这里是多个用户可以收藏多部电影,可以使用关联关系去添加电影,也可以让电影对象有多个收藏用户
@user_bp.route("/cllmv/<int:u_id>-<int:m_id>")
def add_collect(u_id, m_id):
"""
某用户收藏某电影
@param u_id: 电影用户编号
@param m_id: 电影编号
@return:
"""
# 某个用户
user_obj = MovieUserModel.query.filter(MovieUserModel.id == u_id).first()
# 某个电影
movie_obj = MovieModel.query.filter(MovieModel.id == m_id).first()
# 判断用户或电影是否存在
if not (user_obj and movie_obj):
return "请检查输入的用户或电影是否存在!"
# 收藏电影
user_obj.movies.append(movie_obj)
try:
# 提交
db.session.commit()
return "用户收藏成功!"
except:
db.session.rollback() # 回滚
print(traceback.format_exc())
return "用户收藏失败!"
(2)删
删除所有电影用户
@user_bp.route("/deluall")
def del_user_all():
try:
u_query = MovieUserModel.query.all()
for u_obj in list(u_query):
db.session.delete(u_obj)
db.session.commit()
return "删除全部用户成功!"
except:
db.session.rollback() # 回滚
(3)改
@user_bp.route("/updmv")
def update_movie():
# 将前两部电影的名称改为《机械师1》,《机械师2》
try:
for i in range(1, 3):
m = MovieModel.query.get(i)
m.movie_name = f'机械师{i}'
db.session.commit()
return "更新电影信息成功!"
except:
db.session.rollback() # 回滚
print(traceback.format_exc())
return "更新电影信息失败!"
(4)查
@user_bp.route("/getmv")
def get_collect():
# 查询id为8的用户收藏的电影
user8_movie_info = MovieUserModel.query.get(8).movies
try:
return f"id为8的用户收藏了{','.join([mv_obj.movie_name for mv_obj in user8_movie_info])}!"
except:
db.session.rollback() # 回滚
print(traceback.format_exc())
return f"查询id为8的用户收藏电影的信息失败!"
The end !
-------------------------我是有底线的 !!-------------------------------------------------