Проблема пирамиды / SQLAlchemy с объединенными моделями

#python #sqlalchemy #pyramid

#python #sqlalchemy #пирамида

Вопрос:

Я действительно новичок в Python и новичок в Pyramid (это первое, что я написал на Python), и у меня возникли проблемы с запросом к базе данных…

У меня есть следующие модели (в любом случае, относящиеся к моему вопросу):

  • MetadataRef (содержит информацию о заданном типе метаданных)
  • Метаданные (содержит фактические метаданные) — это дочерний элемент MetadataRef
  • Пользователь (содержит пользователей) — это связано с метаданными. MetadataRef.model = ‘User’ и metadata.model_id = user.id

Мне нужен доступ к имени из MetadataRef и значению из метаданных.

Вот мой код:

 class User(Base):
    ...
    _meta = None

    def meta(self):
        if self._meta == None:
            self._meta = {}
            try:
                for item in DBSession.query(MetadataRef.key, Metadata.value).
                    outerjoin(MetadataRef.meta).
                    filter(
                        Metadata.model_id == self.id,
                        MetadataRef.model == 'User'
                    ):
                    self._meta[item.key] = item.value

            except DBAPIError:
                #@TODO: actually do something with this
                self._meta = {}
        return self._meta
  

Генерируемый SQLAlchemy запрос возвращает то, что мне нужно (в любом случае, достаточно близко — ему нужно запросить model_id как часть предложения ON, а не WHERE , но это незначительно, и я почти уверен, что смогу разобраться с этим сам):

 SELECT metadata_refs.`key` AS metadata_refs_key, metadata.value AS metadata_value 
FROM metadata_refs LEFT OUTER JOIN metadata ON metadata_refs.id = metadata.metadata_ref_id 
WHERE metadata.model_id = %s AND metadata_refs.model = %s
  

Однако, когда я обращаюсь к объектам, я получаю эту ошибку:

 AttributeError: 'KeyedTuple' object has no attribute 'metadata_value'
  

Это наводит меня на мысль, что есть какой-то другой способ, которым мне нужно получить к нему доступ, но я не могу понять, как. Я пробовал оба .value и .metadata_value . .key работает так, как ожидалось.

Есть идеи?

Ответ №1:

Вы запрашиваете отдельные атрибуты («дескрипторы с поддержкой ORM» в документах SA):

 DBSession.query(MetadataRef.key, Metadata.value)
  

в этом случае запрос возвращает не полные объекты, отображенные ORM, а KeyedTuple, который представляет собой нечто среднее между кортежем и объектом с атрибутами, соответствующими «меткам» полей.

Итак, один из способов доступа к данным — это их индекс:

 ref_key = item[0]
metadata_value = item[1]
  

В качестве альтернативы, чтобы заставить SA использовать определенное имя для столбца, вы можете использовать метод Column.label():

 for item in DBSession.query(MetadataRef.key.label('ref_key'), Metadata.value.label('meta_value'))...
    self._meta[item.key] = item.meta_value
  

Для отладки вы можете использовать метод Query.column_descriptions(), который сообщит вам имена столбцов, возвращаемых запросом.

Комментарии:

1. О, я не понимал, что к нему нужно обращаться по числовому индексу — мне не нравится зависеть от порядка столбцов, если я хочу добавить другие произвольные столбцы в запрос, поэтому ваше альтернативное решение — это именно то, что я ищу, спасибо!

2. Вам не нужно обращаться к нему через числовой индекс — кортеж с ключом позволяет получить доступ и через obj.attribute синтаксис — вторая половина моего ответа объясняет, как заставить его работать.