Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
543 views
in Technique[技术] by (71.8m points)

python - SQLAlchemy: Query filter including extra data from association table

I am trying to build an ORM mapped SQLite database. The conception of the DB seems to work as intended but I can't seem to be able to query it properly for more complex cases. I have spent the day trying to find an existing answer to my question but nothing works. I am not sure if the issue is with my mapping, my query or both. Or if maybe querying with attributes from a many to many association table with extra data works differently.

This the DB setup:

engine = create_engine('sqlite:///')
Base = declarative_base(bind=engine)
Session = sessionmaker(bind=engine)


class User(Base):
    __tablename__ = 'users'

    # Columns
    id = Column('id', Integer, primary_key=True)
    first = Column('first_name', String(100))
    last = Column('last_name', String(100))
    age = Column('age', Integer)
    quality = Column('quality', String(100))
    unit = Column('unit', String(100))

    # Relationships
    cases = relationship('UserCaseLink', back_populates='user_data')

    def __repr__(self):
        return f"<User(first='{self.first}', last='{self.last}', quality='{self.quality}', unit='{self.unit}')>"


class Case(Base):
    __tablename__ = 'cases'

    # Columns
    id = Column('id', Integer, primary_key=True)
    num = Column('case_number', String(100))
    type = Column('case_type', String(100))

    # Relationships
    users = relationship('UserCaseLink', back_populates='case_data')

    def __repr__(self):
        return f"<Case(num='{self.num}', type='{self.type}')>"


class UserCaseLink(Base):
    __tablename__ = 'users_cases'

    # Columns
    user_id = Column('user_id', Integer, ForeignKey('users.id'), primary_key=True)
    case_id = Column('case_id', Integer, ForeignKey('cases.id'), primary_key=True)
    role = Column('role', String(100))

    # Relationships
    user_data = relationship('User', back_populates='cases')
    case_data = relationship('Case', back_populates='users')


if __name__ == '__main__':
    Base.metadata.create_all()
    session = Session()

and I would like to retrieve all the cases on which a particular person is working under a certain role. So for example I want a list of all the cases a person named 'Alex' is working on as an 'Administrator'. In other words I would like the result of this query:

SELECT [cases].*,
       [main].[users_cases].role
FROM   [main].[cases]
       INNER JOIN [main].[users_cases] ON [main].[cases].[id] = [main].[users_cases].[case_id]
       INNER JOIN [main].[users] ON [main].[users].[id] = [main].[users_cases].[user_id]
WHERE  [main].[users].[first_name] = 'Alex'
AND [main].[users_cases].[role] = 'Administrator';

So far I have tried many things along the lines of:

cases = session.query(Case).filter(User.first == 'Alex', UserCaseLink.role == 'Administrator')

but it is not working as I would like it to.

How can I modify the ORM mapping so that it does the joining for me and allows me to query easily (something like the query I tried)?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

According to your calsses, the quivalent query for:

SELECT [cases].*,
       [main].[users_cases].role
FROM   [main].[cases]
       INNER JOIN [main].[users_cases] ON [main].[cases].[id] = [main].[users_cases].[case_id]
       INNER JOIN [main].[users] ON [main].[users].[id] = [main].[users_cases].[user_id]
WHERE  [main].[users].[first_name] = 'Alex'
AND [main].[users_cases].[role] = 'Administrator';

is

cases = session.query(
    Case.id, Case.num,Cas.type,
    UserCaseLink.role
    ).filter(
    (Case.id==UserCaseLink.case_id)
    &(User.id==UserCaseLink.user_id)
    &(User.first=='Alex')
    &(UserCaseLink.role=='Administrator'
    ).all()

also, you can:

cases = Case.query
    .join(UserCaseLink,Case.id==UserCaseLink.case_id)
    .join(User,User.id==UserCaseLink.user_id)
    .filter( (User.first=='Alex') & (User.first=='Alex') )
    .all()

Good Luck

After comment

based in your comment, I think you want something like:

cases = Case.query
    .filter( (Case.case_data.cases.first=='Alex') & (Case.case_data.cases.first=='Alex') )
    .all()

where case_data connect between Case an UserCaseLink and cases connect between UserCaseLink and User as in your relations.

But,that case causes error: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with dimpor.org_type has an attribute 'org_type_id' The missage shows that the attributes combined in filter should belong to the table class


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...