按相关元素的可选列表过滤元素

Filter elements by optional lists of related elements

我有一种感觉,我把事情变得比他们需要的更复杂了——这不可能是一种罕见的情况。在我看来,这应该是可能的 - 或者也许我做的事情从根本上是错误的。

手头的问题是这样的:我已经声明了一个数据库元素,Element,它由大约 10 个与其他元素的多对多关系组成,其中之一是 Tag .

我想让我的应用程序的用户能够通过所有这些关系过滤 Element,其中一些关系或其中的 none。假设用户只想查看与某个 Tag.

相关的 Elements

为了让事情变得更加困难,执行这个 objective 的函数是从 graphql API 调用的,这意味着它将接收 ID 而不是 ORM 对象。

我正在尝试在我的 Python Flask 项目中构建解析器,使用 SQLAlchemy,它将提供如下接口:

# graphql request
query getElements {
  getElements(tags:[2, 3] people:[8, 13]) {
    id
  }
}

# graphql response
{
  "data": {
    "getElements": [
      {
        "id": "2"
      },
      {
        "id": "3"
      },
      {
        "id": "8"
      }
    ]
  }
}

我想象解析器看起来像这个简化的伪代码,但我终究无法弄清楚如何实现它:

def get_elements(tags=None, people=None):
  args = {'tags' : tags, 'people' : people}
  if any(args):
    data_elements = DataElement.query.filter_by(this in args) # this is the tricky bit - for each of DataElements related elements, I want to check if its ID is given in the corresponding argument
  else:
    data_elements = DataElement.query.all()
  
  return data_elements

这是应要求对简化数据库模型的一瞥。 DataElement 拥有很多这样的关系,并且它完美地工作:

class DataElement(db.Model):
  __tablename__ = 'DataElement'
  id = db.Column(db.Integer, primary_key=True)
  tags = db.relationship('Tag', secondary=DataElementTag, back_populates='data_elements')

class Tag(db.Model):
  __tablename__ = 'Tag'
  id = db.Column(db.Integer, primary_key=True)
  data_elements = db.relationship('DataElement', secondary=DataElementTag, back_populates='tags')

DataElementTag = db.Table('DataElementTag',
  db.Column('id', db.Integer, primary_key=True),
  db.Column('data_element_id', db.Integer, db.ForeignKey('DataElement.id')),
  db.Column('tag_id', db.Integer, db.ForeignKey('Tag.id'))
)

拜托,ORM 奇才和 python 怪胎,我向你求助!

我以一种相当笨拙的方式解决了它。我想一定有更优雅的方法来解决这个问题,我仍在等待更好的答案。

我最终遍历了所有给定的参数并使用 eval()(不是用户输入,不用担心)来获取相应的数据库模型。从那里,我能够获取具有多对多关系的 DataElement 对象。我的最终解决方案如下所示:

args = {
        'status' : status,
        'person' : people,
        'tag' : tags,
        'event' : events,
        'location' : locations,
        'group' : groups,
        'year' : year
    } # dictionary for args for easier data handling

if any(args.values()):
        final = [] # will contain elements matching criteria

        for key, value in args.items():
            if value:
                model = eval(key.capitalize()) # get ORM model from dictionary key name (eval used on hardcoded string, hence safe)
                for id in value:
                    filter_element = model.query.filter_by(id=id).one_or_none() # get the element in question from db
                    if filter_element:
                        elements = filter_element.data_elements # get data_elements linked to element in question
                        for element in elements:
                            if not element in final: # to avoid duplicates
                                final.append(element)

        return final