簡體   English   中英

SQLAlchemy:從連接多個表中獲取單個對象

[英]SQLAlchemy: Getting a single object from joining multiple tables

數據庫 - SQLAlchemy 設置

考慮兩個表的經典設置 - userapi_key ,由 SQLAlchemy 對象表示為:

class User(Base):
    __tablename__ = 'user'
    user_id = Column(String)
    user_name = Column(String)
    vioozer_api_key = Column(String, ForeignKey("api_key.api_key"))

class ApiKey(Base):
    __tablename__ = 'api_key'    
    api_key = Column(String(37), primary_key=True)

為清楚起見,省略了其他字段。

我的查詢

假設我想獲取特定用戶 ID 的用戶名和 api 密鑰:

user, api_key = database.db_session.query(User, ApiKey)\
    .join(ApiKey, User.vioozer_api_key==ApiKey.api_key)\
    .filter(User.user_id=='user_00000000000000000000000000000000').first()

我得到兩個對象 - userapi_key ,我可以從中獲取user.nameapi_key.api_key

問題

我想用一個函數包裝這個調用,該函數將返回一個對象,其字段將是user字段和api_key字段的聯合 - 與 SQL join返回一個表的方式相同,兩個表的列都被連接。 有沒有辦法自動獲取一個對象,其字段是兩個表的字段的並集?

我試過什么

我可以為每個 Join 操作定義一個映射器類,但我想知道映射是否可以自動完成。

我很驚訝關於這個的討論很少......我需要連接兩個表並返回一個包含兩個表中所有列的單個對象。 我希望這是一個非常常見的用例 - 無論如何,這是我經過數小時尋找但未能找到好的方法后所做的黑客攻擊

from sqlalchemy import inspect
def obj_to_dict(obj):
    return {c.key: getattr(obj, c.key) for c in inspect(obj).mapper.column_attrs}
def flatten_join(tup_list):
    return [{**obj_to_dict(a), **obj_to_dict(b)} for a,b in tup_list]

這是它的作用的一個例子......

########################## SETUP ##########################
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, Integer, ForeignKey, Text
from sqlalchemy.ext.declarative import declarative_base

DeclarativeBase = declarative_base()


class Base(DeclarativeBase):
    __abstract__ = True

    def __repr__(self) -> str:
        items = []
        for key in self.__table__._columns._data.keys():
            val = self.__getattribute__(key)
            items.append(f'{key}={val}')
        key_vals = ' '.join(items)
        name = self.__class__.__name__
        return f"<{name}({key_vals})>"


class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True, index=True)
    username = Column(Text)


class Todo(Base):
    __tablename__ = "todo"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(Text)
    user_id = Column(Integer, ForeignKey("user.id"))


engine = create_engine("sqlite:///:memory:")
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
Base.metadata.create_all(bind=engine)

########################## DEMO ##########################

db.add(User(**{"id": 1, "username": "jefmason"}))
db.add(Todo(**{"id": 1, "title": "Make coffee", "user_id": 1}))
db.add(Todo(**{"id": 2, "title": "Learn SQLAlchemy", "user_id": 1}))
db.commit()

result = db.query(Todo, User).join(User).all()

print(result)
# > [(<Todo(id=1 title=Make coffee user_id=1)>, <User(id=1 username=jefmason)>), (<Todo(id=2 title=Learn SQLAlchemy user_id=1)>, <User(id=1 username=jefmason)>)]

print(flatten_join(result))
# > [{'id': 1, 'title': 'Make coffee', 'user_id': 1, 'username': 'jefmason'}, {'id': 1, 'title': 'Learn SQLAlchemy', 'user_id': 1, 'username': 'jefmason'}]

不是查詢對象,而是查詢字段列表,在這種情況下,SQLAlchemy 返回KeyedTuple實例,它提供KeyedTuple._asdict()方法,您可以使用它返回任意字典:

def my_function(user_id):
    row =  database.db_session.query(User.name, ApiKey.api_key)\
        .join(ApiKey, User.vioozer_api_key==ApiKey.api_key)\
        .filter(User.user_id==user_id).first()
    return row._asdict()

my_data = my_function('user_00000000000000000000000000000000')

但是對於您的特定查詢,您甚至不需要加入ApiKey因為api_key字段存在於User表中:

row = database.db_session.query(User.name, User.api_key)\
    .filter(User.user_id==user_id).first()

我會在User上添加一個 api_key 關系:

class User(Base):
    __tablename__ = 'user'
    user_id = Column(String)
    user_name = Column(String)
    vioozer_api_key = Column(String, ForeignKey("api_key.api_key"))
    api_key = Relationship('ApiKey', uselist=False)

然后你可以做這樣的查詢:

>>> user = database.db_session.query(User)\
      .filter(User.user_id=='user_00000000000000000000000000000000').first()
>>> user.api_key
<ApiKey>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM