python - SQLAlchemy M2M关系查询

标签 python sqlalchemy

我有以下模型,我正在尝试列出在用户组中有一个组的所有最新文档版本。

class User(Base, BaseModel):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(Unicode(EMAIL_LENGTH), unique=True, nullable=False)
    full_name = Column(Unicode(FULL_NAME_LENGTH), nullable=False)
    deleted = Column(Boolean, nullable=False, default=False)

    document_versions = relationship(
        'DocumentVersion', 
        backref="author")


class Group(Base, BaseModel):
    __tablename__ = 'groups'
    __table_args__ = (UniqueConstraint('name','company_id'),)
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(60), nullable=False)
    deleted = Column(Boolean, nullable=False, default=False)

    # Companies have many groups
    company_id = Column(
            Integer,
            ForeignKey('companies.id'),
            nullable=False)
    roles = relationship("Role",
            primaryjoin="Group.id==Role.group_id",
            backref="group")
    users = relationship("User",
            secondary=group_user,
            backref="groups")

document_version_group = Table(
        "document_version_groups",
        Base.metadata,
        Column('group_id', Integer, ForeignKey('groups.id')),
        Column('document_version_id', Integer, ForeignKey('document_versions.id')))

class DocumentVersion(Base, BaseModel):
    __tablename__ = 'document_versions'
    id = Column(Integer, primary_key=True)
    document_id = Column(
            Integer,
            ForeignKey('documents.id'),
            nullable=False)

    author_id = Column(
            Integer,
            ForeignKey('users.id'),
            nullable=False)
    groups = relationship(
            "Group",
            secondary=document_version_group,
            lazy='dynamic', # This is newly added just now
            backref="document_versions")
    status = Column(
            Enum(
                "draft",
                "review",
                "approval",
                "declined",
                "approved",
                name="status"),
            default="draft",
            nullable=False)

我尝试了以下但没有成功(注意返回的 document_version 为零组)

>>> docs = session.query(DocumentVersion).filter(Document.company == user.company).filter(Group.id.in_([g.id for g in user.groups])).all()

2012-08-31 15:14:19,651 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT document_versions.id AS document_versions_id, document_versions.document_id AS document_versions_document_id, document_versions.document_current_draft_id AS document_versions_document_current_draft_id, document_versions.document_current_active_id AS document_versions_document_current_active_id, document_versions.author_id AS document_versions_author_id, document_versions.status AS document_versions_status, document_versions.major_version AS document_versions_major_version, document_versions.minor_version AS document_versions_minor_version 
FROM document_versions, documents, groups 
WHERE %(param_1)s = documents.company_id AND groups.id IN (%(id_1)s)
2012-08-31 15:14:19,651 INFO  [sqlalchemy.engine.base.Engine][MainThread] {'id_1': 1, 'param_1': 1}

>>> [d.groups.all() for d in docs]

2012-08-31 15:15:29,996 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT groups.id AS groups_id, groups.name AS groups_name, groups.deleted AS groups_deleted, groups.company_id AS groups_company_id 
FROM groups, document_version_groups 
WHERE %(param_1)s = document_version_groups.document_version_id AND groups.id = document_version_groups.group_id
2012-08-31 15:15:29,996 INFO  [sqlalchemy.engine.base.Engine][MainThread] {'param_1': 1}
2012-08-31 15:15:29,998 INFO  [sqlalchemy.engine.base.Engine][MainThread] SELECT groups.id     AS groups_id, groups.name AS groups_name, groups.deleted AS groups_deleted, groups.company_id AS groups_company_id 
FROM groups, document_version_groups 
WHERE %(param_1)s = document_version_groups.document_version_id AND groups.id = document_version_groups.group_id
2012-08-31 15:15:29,998 INFO  [sqlalchemy.engine.base.Engine][MainThread] {'param_1': 2}
[[], [<project.models.Group object at 0x27ead50>]]

最佳答案

假设此 SQL 给出正确的结果:

SELECT *
FROM document_versions
WHERE
    EXISTS (
        SELECT 1
        FROM documents
        WHERE
            document_versions.document_id = documents.id
            AND documents.company_id = :company_id
    )
    AND EXISTS (
        SELECT 1
        FROM document_version_groups
        WHERE
            document_versions.id = document_version_groups.document_version_id
            AND document_version_groups.group_id IN :group_ids
    )

那么下面的 SQLAlchemy 表达式应该可以工作:

session.query(DocumentVersion).filter(and_(
    DocumentVersion.document.has(compay=user.company),
    DocumentVersion.groups.any(
        Group.id.in_([g.id for g in user.groups])
    ),
)).all()

请注意关系属性 DocumentVersion.documentDocumentVersion.groups 的显式使用。没有它们,查询将只包含一堆不相关的表,从而给您误报结果。

关于python - SQLAlchemy M2M关系查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12217625/

相关文章:

python - 快速将内存中的数据库转储到文件

python - 在 SQLAlchemy 中优化包含多条记录的 Update 语句

python - 哪个 Python (sqlalchemy) mssql DB API 在 Cygwin 中工作?

python - 读取串口的子进程 Read

python - 在 apache(linux 和 windows)上运行 python 脚本

python - 减少绘图刻度数

Python正则表达式拆分函数多个分隔符

security - 在 Pyramid 中,您可以在类和该类的实例上都拥有 ACL

python - 属性错误 : 'NoneType' object has no attribute '_instantiate_plugins' while creating engine

python - 在 python 程序中引发手动异常会终止它吗?