Как выбрать одну из таблиц A на основе таблицы B в SQLAlchemy?

#python #sqlalchemy

#python #sqlalchemy

Вопрос:

У меня есть две таблицы Items и ItemDescriptions

 class Item(Base):
    __tablename__ = "item"
    id = Column(Integer, primary_key=True)
    description_id = Column(Integer, ForeignKey('item_description.id'))

class ItemDescription(Base):
   __tablename__ = "item_description"
   id = Column(Integer, primary_key=True)
  

Учитывая список ItemDescriptions, я хочу получить список элементов таким образом, чтобы в каждом идентификаторе ItemDescription было по одному элементу. Мне все равно, какой элемент.

[Отредактировано для наглядности]

Учитывая этот список элементов и описаний:

 Item, Description
1   , 1
2   , 1
3   , 1
4   , 2
5   , 2
6   , 3
7   , 3
8   , 3
  

Я хочу запрос, который вернет что-то вроде:

 Item, Description
2   , 1
4   , 2
7   , 3   
  

У меня возникли проблемы с обработкой подзапросов и т.д.

Спасибо за вашу помощь

Ответ №1:

Я большой поклонник column_property. Вот способ сделать то, что вы хотите, с помощью column_property:

 from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Item(Base):
    __tablename__ = 'item'

    id = Column(Integer, primary_key=True)
    description_id = Column(Integer, ForeignKey('item_description.id'))

class ItemDescription(Base):
    __tablename__ = 'item_description'

    id = Column(Integer, primary_key=True)

    any_item_id = column_property(
        select(
            [Item.id],
            id == Item.description_id,
            limit=1,
        ).label('any_item_id'),
        deferred=True,
    )

e = create_engine('sqlite://', echo=True)
Base.metadata.create_all(e)

s = Session(e)

descriptions = [
    ItemDescription(id=1),
    ItemDescription(id=2),
    ItemDescription(id=3),
]

s.add_all(descriptions)

items = [
    Item(id=1, description_id=1),
    Item(id=2, description_id=1),
    Item(id=3, description_id=1),
    Item(id=4, description_id=2),
    Item(id=5, description_id=2),
    Item(id=6, description_id=3),
    Item(id=7, description_id=3),
    Item(id=8, description_id=3),
]

s.add_all(items)

query = s.query(ItemDescription).options(undefer('any_item_id'))
for description in query:
    print description.any_item_id, description.id

# alternative way without using column_property
query = s.query(
    select(
        [Item.id],
        ItemDescription.id == Item.description_id,
        limit=1,
    ).label('any_item_id'),
    ItemDescription,
)
for item_id, description in query:
    print item_id, description.id
  

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

1. column_property выглядит великолепно, спасибо! Если бы я хотел сделать это как отдельный запрос, а не свойство столбца, поместил бы я select в подзапрос?

2. Да, вы можете переместить select в запрос. Я добавил это к ответу. Лично я все еще предпочитаю column_property, хотя 🙂

3. Мне нравится метод column_query, но спасибо, что также показали способ запроса in.

Ответ №2:

 from sqlalchemy.orm import relationship, backref

class Item(Base):
    __tablename__ = "item"
    id = Column(Integer, primary_key=True)
    description_id = Column(Integer, ForeignKey('item_description.id'))
    desc = relationship(User, backref=backref('desc', order_by=id))

class ItemDescription(Base):
   __tablename__ = "item_description"
   id = Column(Integer, primary_key=True)

Now your every ItemDescription class will have an backref called `desc` which is nothing but a list of Item.

Now you can do something like this

item_desc = session.query(ItemDescription).
...                        options(joinedload('desc').all()

for item in item_desc:
    print item.desc
  

Я думаю, что это может не дать вам точного ответа. я думаю, это поможет

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

1. Извините, мое объяснение не очень понятно, я отредактирую его, чтобы сделать более понятным.