通过关联建立多对多关系
Many to many relationship through association
我有以下型号:
群组:
class Group(db.Model):
__tablename__ = 'Groups'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True, unique=True)
description = db.Column(db.Text, nullable=False)
created_on = db.Column(db.DateTime, default=datetime.now)
updated_on = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
users = db.relationship(
"User",
secondary=users_groups_assocation_table,
back_populates="groups")
analysis = db.relationship(
"Analysis",
secondary=analysis_groups_assocation_table,
back_populates="groups"
用户:
class User(db.Model):
__tablename__ = 'Users'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
alias = db.Column(db.String(120), index=True, unique=True)
name = db.Column(db.String(120), index=True, unique=False)
lastname = db.Column(db.String(120), index=True, nullable=False)
email = db.Column(db.String(120), index=True, nullable=False)
password = db.Column(db.String(120), index=False, nullable=True)
created_on = db.Column(db.DateTime, default=datetime.now)
updated_on = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
admin = db.Column(db.Boolean, default=False)
local_auth = db.Column(db.Boolean, default=False)
override_tableau = db.Column(db.Boolean, default=False)
groups = db.relationship(
"Group",
secondary=users_groups_assocation_table,
back_populates="users")
分析:
class Analysis(db.Model):
__tablename__ = 'Analysis'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True, unique=True)
description = db.Column(db.Text, nullable=False)
embed_analysis = db.Column(db.Text, nullable=True)
service_account = db.Column(db.Text, nullable=True)
img = db.Column(db.LargeBinary(length=(2**32)-1), nullable=True)
img_mimetype = db.Column(db.Text, nullable=True)
img_name = db.Column(db.Text, nullable=True)
category_id = db.Column(db.Integer, db.ForeignKey('Categories.id'), nullable=True)
created_on = db.Column(db.DateTime, default=datetime.now)
updated_on = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
category = db.relationship("Category", back_populates="analysis")
draft = db.Column(db.Boolean, default=False)
groups = db.relationship(
"Group",
secondary=analysis_groups_assocation_table,
back_populates="analysis")
关联表:
users_groups_assocation_table = db.Table('users_groups',
db.Column('user_id', db.Integer, db.ForeignKey('Users.id')),
db.Column('group_id', db.Integer, db.ForeignKey('Groups.id'))
)
analysis_groups_assocation_table = db.Table('analysis_groups',
db.Column('analysis_id', db.Integer, db.ForeignKey('Analysis.id')),
db.Column('group_id', db.Integer, db.ForeignKey('Groups.id'))
)
所以在用户<->分析之间存在隐含的多对多关系。如何通过 groups
创建这样的关联?来自 ruby on rails 在这种情况下有一个 :through 关键字,flask sqlalchemy 是否有类似的东西?
我想要 User.query.first().analysis
sqlalchemy 的关系可以做很多事情,但我发现复杂的关系很难获得 right/predictable 并且一次性查询更容易维护(至少对于复杂的关系)。
我试图解决您询问的关系,如 User.analyses
但在末尾包含了一个一次性查询。
我在此处的文档中使用了跨多个表的简化版本:https://docs.sqlalchemy.org/en/14/orm/join_conditions.html#composite-secondary-join
请注意,连接混合使用了 Table
对象,这些对象需要引用 .c.
的列并映射 类 ,例如直接引用列的 Analysis
。
您可能还需要 relationship
的 order_by
参数,否则这种关系可能毫无意义。
from datetime import datetime, date
from sqlalchemy import (
create_engine,
Text,
Integer,
String,
ForeignKey,
UniqueConstraint,
update,
DateTime,
Date,
Boolean,
LargeBinary,
)
from sqlalchemy.schema import (
Table,
Column,
MetaData,
)
from sqlalchemy.sql import select
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
Base = declarative_base()
engine = create_engine("sqlite://", echo=False)
users_groups_table = Table('users_groups', Base.metadata,
Column('user_id', Integer, ForeignKey('Users.id')),
Column('group_id', Integer, ForeignKey('Groups.id'))
)
analysis_groups_table = Table('analysis_groups', Base.metadata,
Column('analysis_id', Integer, ForeignKey('Analysis.id')),
Column('group_id', Integer, ForeignKey('Groups.id'))
)
class Group(Base):
__tablename__ = 'Groups'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True)
name = Column(String(120), index=True, unique=True)
users = relationship(
"User",
secondary=users_groups_table,
back_populates="groups")
analysis = relationship(
"Analysis",
secondary=analysis_groups_table,
back_populates="groups")
class User(Base):
__tablename__ = 'Users'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True)
name = Column(String(120), index=True, unique=False)
groups = relationship(
"Group",
secondary=users_groups_table,
back_populates="users")
analyses = relationship("Analysis",
# The middle
secondary="join(users_groups, analysis_groups, users_groups.c.group_id == analysis_groups.c.group_id)",
# Join from left to the middle
primaryjoin="User.id == users_groups.c.user_id",
# Join from right to the middle
secondaryjoin="Analysis.id == analysis_groups.c.analysis_id",
uselist=True,
viewonly=True
)
class Analysis(Base):
__tablename__ = 'Analysis'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True)
name = Column(String(120), index=True, unique=True)
groups = relationship(
"Group",
secondary=analysis_groups_table,
back_populates="analysis")
Base.metadata.create_all(engine)
with Session(engine) as session:
users = {}
for name in ('a', 'b'):
users[name] = User(name=name)
session.add(users[name])
groups = {}
for name in ('a', 'b'):
groups[name] = Group(name=name)
session.add(groups[name])
analyses = {}
for name in ('x', 'y', 'z'):
analyses[name] = Analysis(name=name)
session.add(analyses[name])
groups['a'].users.append(users['a'])
groups['b'].users.append(users['b'])
analyses['x'].groups.append(groups['a'])
analyses['y'].groups.append(groups['b'])
analyses['z'].groups.append(groups['b'])
session.commit()
print (users['a'].analyses[0].name)
# One-off ad-hoc query.
q = session.query(Analysis).join(Analysis.groups).join(Group.users).filter(User.id == users['a'].id)
print (q)
res = q.first()
print (res.name)
我有以下型号:
群组:
class Group(db.Model):
__tablename__ = 'Groups'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True, unique=True)
description = db.Column(db.Text, nullable=False)
created_on = db.Column(db.DateTime, default=datetime.now)
updated_on = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
users = db.relationship(
"User",
secondary=users_groups_assocation_table,
back_populates="groups")
analysis = db.relationship(
"Analysis",
secondary=analysis_groups_assocation_table,
back_populates="groups"
用户:
class User(db.Model):
__tablename__ = 'Users'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
alias = db.Column(db.String(120), index=True, unique=True)
name = db.Column(db.String(120), index=True, unique=False)
lastname = db.Column(db.String(120), index=True, nullable=False)
email = db.Column(db.String(120), index=True, nullable=False)
password = db.Column(db.String(120), index=False, nullable=True)
created_on = db.Column(db.DateTime, default=datetime.now)
updated_on = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
admin = db.Column(db.Boolean, default=False)
local_auth = db.Column(db.Boolean, default=False)
override_tableau = db.Column(db.Boolean, default=False)
groups = db.relationship(
"Group",
secondary=users_groups_assocation_table,
back_populates="users")
分析:
class Analysis(db.Model):
__tablename__ = 'Analysis'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True, unique=True)
description = db.Column(db.Text, nullable=False)
embed_analysis = db.Column(db.Text, nullable=True)
service_account = db.Column(db.Text, nullable=True)
img = db.Column(db.LargeBinary(length=(2**32)-1), nullable=True)
img_mimetype = db.Column(db.Text, nullable=True)
img_name = db.Column(db.Text, nullable=True)
category_id = db.Column(db.Integer, db.ForeignKey('Categories.id'), nullable=True)
created_on = db.Column(db.DateTime, default=datetime.now)
updated_on = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
category = db.relationship("Category", back_populates="analysis")
draft = db.Column(db.Boolean, default=False)
groups = db.relationship(
"Group",
secondary=analysis_groups_assocation_table,
back_populates="analysis")
关联表:
users_groups_assocation_table = db.Table('users_groups',
db.Column('user_id', db.Integer, db.ForeignKey('Users.id')),
db.Column('group_id', db.Integer, db.ForeignKey('Groups.id'))
)
analysis_groups_assocation_table = db.Table('analysis_groups',
db.Column('analysis_id', db.Integer, db.ForeignKey('Analysis.id')),
db.Column('group_id', db.Integer, db.ForeignKey('Groups.id'))
)
所以在用户<->分析之间存在隐含的多对多关系。如何通过 groups
创建这样的关联?来自 ruby on rails 在这种情况下有一个 :through 关键字,flask sqlalchemy 是否有类似的东西?
我想要 User.query.first().analysis
sqlalchemy 的关系可以做很多事情,但我发现复杂的关系很难获得 right/predictable 并且一次性查询更容易维护(至少对于复杂的关系)。
我试图解决您询问的关系,如 User.analyses
但在末尾包含了一个一次性查询。
我在此处的文档中使用了跨多个表的简化版本:https://docs.sqlalchemy.org/en/14/orm/join_conditions.html#composite-secondary-join
请注意,连接混合使用了 Table
对象,这些对象需要引用 .c.
的列并映射 类 ,例如直接引用列的 Analysis
。
您可能还需要 relationship
的 order_by
参数,否则这种关系可能毫无意义。
from datetime import datetime, date
from sqlalchemy import (
create_engine,
Text,
Integer,
String,
ForeignKey,
UniqueConstraint,
update,
DateTime,
Date,
Boolean,
LargeBinary,
)
from sqlalchemy.schema import (
Table,
Column,
MetaData,
)
from sqlalchemy.sql import select
from sqlalchemy.orm import declarative_base, relationship
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
Base = declarative_base()
engine = create_engine("sqlite://", echo=False)
users_groups_table = Table('users_groups', Base.metadata,
Column('user_id', Integer, ForeignKey('Users.id')),
Column('group_id', Integer, ForeignKey('Groups.id'))
)
analysis_groups_table = Table('analysis_groups', Base.metadata,
Column('analysis_id', Integer, ForeignKey('Analysis.id')),
Column('group_id', Integer, ForeignKey('Groups.id'))
)
class Group(Base):
__tablename__ = 'Groups'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True)
name = Column(String(120), index=True, unique=True)
users = relationship(
"User",
secondary=users_groups_table,
back_populates="groups")
analysis = relationship(
"Analysis",
secondary=analysis_groups_table,
back_populates="groups")
class User(Base):
__tablename__ = 'Users'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True)
name = Column(String(120), index=True, unique=False)
groups = relationship(
"Group",
secondary=users_groups_table,
back_populates="users")
analyses = relationship("Analysis",
# The middle
secondary="join(users_groups, analysis_groups, users_groups.c.group_id == analysis_groups.c.group_id)",
# Join from left to the middle
primaryjoin="User.id == users_groups.c.user_id",
# Join from right to the middle
secondaryjoin="Analysis.id == analysis_groups.c.analysis_id",
uselist=True,
viewonly=True
)
class Analysis(Base):
__tablename__ = 'Analysis'
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True)
name = Column(String(120), index=True, unique=True)
groups = relationship(
"Group",
secondary=analysis_groups_table,
back_populates="analysis")
Base.metadata.create_all(engine)
with Session(engine) as session:
users = {}
for name in ('a', 'b'):
users[name] = User(name=name)
session.add(users[name])
groups = {}
for name in ('a', 'b'):
groups[name] = Group(name=name)
session.add(groups[name])
analyses = {}
for name in ('x', 'y', 'z'):
analyses[name] = Analysis(name=name)
session.add(analyses[name])
groups['a'].users.append(users['a'])
groups['b'].users.append(users['b'])
analyses['x'].groups.append(groups['a'])
analyses['y'].groups.append(groups['b'])
analyses['z'].groups.append(groups['b'])
session.commit()
print (users['a'].analyses[0].name)
# One-off ad-hoc query.
q = session.query(Analysis).join(Analysis.groups).join(Group.users).filter(User.id == users['a'].id)
print (q)
res = q.first()
print (res.name)