sqlalchemy查询与sqlite的一对多关系

时间:2017-01-18 23:10:21

标签: python sqlite sqlalchemy

我已经编写了以下示例代码来为我正在编写的ACL系统构建搜索索引。此示例中的查询将重新包含分配了任何给定ACL的所有对象。但是我需要一个查询/过滤器来返回分配了所有ACL的对象。

感谢任何帮助。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import backref
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Column

_db_uri = "sqlite:////tmp/test.sql"
Base = declarative_base()
engine = create_engine(_db_uri, echo=False)
Session = sessionmaker(bind=engine)

class IndexObject(Base):
    """ Index object. """
    __tablename__ = 'objects'
    id = Column(Integer, primary_key=True)
    name = Column(String(128), unique=True, nullable=True)
    acls = relationship('IndexObjectACL',
                                cascade = "all,delete",
                                backref='objects',
                                lazy='dynamic')

    def __repr__(self):
        _repr_ =("<IndexObject (name='%s')>" % (self.name))
        return _repr_


class IndexObjectACL(Base):
    """ Index object ACL. """
    __tablename__ = 'acls'
    id = Column(Integer, primary_key=True)
    value = Column(String(128), nullable=False)
    oid = Column(Integer, ForeignKey('objects.id'))

    def __repr__(self):
        __repr__ = ("<IndexObjectACL (value='%s')>" % (self.value))
        return __repr__


object_list = [
        "object1",
        "object2",
        "object3",
    ]

acl_list = {
        "object1" : [
                    "view",
                    "edit",
                    "enable",
                    "delete",
                    ],
        "object2" : [
                    "view",
                    "edit",
                    ],
        "object3" : [
                    "enable",
                    "delete",
                    ],
    }


Base.metadata.create_all(engine)

session = Session()

for o in object_list:
    acls = []
    for acl in acl_list[o]:
        a = IndexObjectACL(value=acl)
        acls.append(a)

    index_object = IndexObject(name=o, acls=acls)
    session.add(index_object)

session.commit()


search_acls = [ "enable", "delete" ]
q = session.query(IndexObject)
q = q.join(IndexObject.acls).filter(IndexObjectACL.value.in_(search_acls))

print(q.all())

session.close()

2 个答案:

答案 0 :(得分:1)

我认为这可能是一种在某种程度上使用division的机会。 IndexObjectACL 除以 SearchAcls 应该产生具有所有 SearchAcls IndexObject 。换句话说,查询 IndexObject ,其中没有 SearchAcls 不存在于 IndexObjectACL 中:

from sqlalchemy import union, select, literal

# Create an aliased UNION of all the search_acls to query against
search_acls_union = union(*(select([literal(acl).label('acl')])
                            for acl in search_acls)).alias()

# Query for those IndexObjects where...
# No SearchAcl exists where...
# No IndexObjectACL exists where value == acl AND oid == id
q = session.query(IndexObject).\
    filter(~session.query().select_from(search_acls_union).
           filter(~IndexObject.acls.any(value=search_acls_union.c.acl)).
           exists())

此查询的结果是

[<IndexObject (name='object1')>, <IndexObject (name='object3')>]

如果你添加

    "object4" : [
                "enable",
                ],
    "object5" : [
                "delete",
                ],

到你的acl_list(以及object_list的对象名称),为了证明没有返回部分匹配,它仍然只返回对象1和3。

你的原作&#34;有任何&#34;也可以重写查询以使用semijoin或SQL中的EXISTS发言:

q = session.query(IndexObject).\
    filter(IndexObject.acls.any(
        IndexObjectACL.value.in_(search_acls)))

答案 1 :(得分:0)

queries = []
acl_q = q.join(IndexObject.acls)
for acl in search_acls:
    x = acl_q.filter(IndexObjectACL.value == acl)
    queries.append(x)
q = q.intersect(*queries)

我可以试着解释一下,但我是sqlalchemy和SQL的新手。所以我可能用错误的方式解释它...... join()根据它们的关系连接IndexObject和IndexObjectACL表,从而产生新的查询。此查询用于为我们要使用filter()匹配的每个ACL创建新查询。最后,我们使用intersect()SQL INTERSECT)来获取所有查询中出现的所有IndexObject。经过一些测试后,似乎这是一种快速搜索已分配所有ACL的对象的方法。它也非常pythonic恕我直言。