过滤sqlalchemy连接的查询,在解析的inpu上构造

2024-09-27 07:23:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我坚持这一点:我采用了bauble (a program on github)并且部分是用来指定sql数据库上的查询。查询语言实际上是三种不同的语言,其中一种(像SQL查询中的过滤)我正在重写。

最初的作者选择了pyparsing,我没有理由回顾这个选择,除了我不知道pyparsing,我一直在与lex和yacc一起玩得很开心。。。但我决定继续解析,所以我在学习它。

我已经(重新)编写了一个解析器,它识别给定的查询,并且大多数语法类别都转换为类。我认为解析部分很好,我卡住的地方是,我用pyparsing创建的对象需要使用SQLAlchemy查询数据库,特别是当我基于连接表的属性进行筛选时。

语法的相关部分,以pyparsing格式:

query_expression = Forward()
identifier = Group(delimitedList(Word(alphas, alphanums+'_'),
                                 '.')).setParseAction(IdentifierToken)
ident_expression = (
    Group(identifier + binop + value).setParseAction(IdentExpressionToken)
    | (
        Literal('(') + query_expression + Literal(')')
    ).setParseAction(ParenthesisedQuery))
query_expression << infixNotation(
    ident_expression,
    [ (NOT_, 1, opAssoc.RIGHT, SearchNotAction),
      (AND_, 2, opAssoc.LEFT,  SearchAndAction),
      (OR_,  2, opAssoc.LEFT,  SearchOrAction) ] )

而最后两个类的对应类(前两个类的^{{cd1>}方法是我还不知道如何编写的):

^{pr2}$

以上代码段的完整和最新代码是here

几个可能的查询:

^{pr3}$

Tags: githubon语法grouppyparsingprogramqueryleft
2条回答

我想我找到了一个暂时可以接受的答案,但是它使用了SQLAlchemy的内部信息(一个带下划线前缀的字段)。在

问题的核心是,由于我处理的是来自用户的解析信息,所以我从一个类的名称和要导航的关系的名称开始。例如在plant where accession.species.id=44中,类名是Plant,我正在对连接的Species对象的id进行过滤。在

上面的例子可能会让人觉得事情很简单,只是一个资本化问题。但是我们仍然需要知道PlantAccession,和{}在哪个模块中被找到。在

另一个例子:family where genera.id!=0。一般来说,关系的名称不必等于所引用的类的名称。在

语法没问题,我不需要再改了。问题是(而且部分仍然是)在与SQLAlchemy的交互中,所以我不得不更正IdentifierToken和{}类中的evaluate方法。在

我的解决方案包括以下代码:

class IdentifierToken(object):
....
    def evaluate(self, env):
        """return pair (query, attribute)

        the value associated to the identifier is an altered query where the
        joinpoint is the one relative to the attribute, and the attribute
        itself.
        """

        query = env.session.query(env.domain)
        if len(self.value) == 1:
            # identifier is an attribute of the table being queried
            attr = getattr(env.domain, self.value[0])
        elif len(self.value) > 1:
            # identifier is an attribute of a joined table
            query = query.join(*self.value[:-1], aliased=True)
            attr = getattr(query._joinpoint['_joinpoint_entity'], self.value[-1])
        return query, attr

class IdentExpressionToken(object):
...
    def evaluate(self, env):
        q, a = self.operands[0].evaluate(env)
        clause = lambda x: self.operation(a, x)
        return q.filter(clause(self.operands[1].express()))

一些要点:

  • 最初我不清楚query方法没有改变调用它的查询,但是我必须使用返回值。在
  • 我对连接的查询取别名,以便很容易检索联接操作的“destination”类。在
  • 对于别名联接查询,我使用的是字段_joinpoint,它看起来像是未公开的信息。在
  • query._joinpoint['_joinpoint_entity']是对类的引用,我需要从中检索解析查询中命名的字段。_joinpoint字典在非别名查询上看起来不同。

  • 这个问题仍然悬而未决的部分是,是否有一种“官方的”SQLAlchemy方法来检索这些信息。

以前的开发人员似乎在创建这些类时费了很大的劲——这实际上是使用pyparsing时的“最佳实践”。其目的是,这些类作为解析过程的输出,通常使用已解析的元素来支持它们自己的一些行为。在本例中,还可以通过名称访问元素(另一个pyparsing“最佳实践”)。一旦这些类在解析过程中被构造出来,pyparsing就不可能了——任何进一步的处理都纯粹是这些类的函数。在

我想我们的目标很可能和你假设的一样,在这些类上有一个方法,比如results.statement.invoke()。看看这些类上的方法,看看它们为您提供了什么,特别是顶级StatementAction类。如果没有这样的方法,那么这可能是下一步,您将以对SQLAlchemy数据库包装有意义的方式应用解析的值。在

相关问题 更多 >

    热门问题