😸

sqlalchemyで検索値指定によるテーブルリレーションを動的に行う方法

2024/09/21に公開

はじめに

sqlalchemyを使用して、リクエストで検索値のカラムを文字列で指定する場合、その対象のテーブルをリレーションして値を参照しに行く必要があります。
それをclassを使って動的に実装する方法を紹介します。

実際のコード

まずは検索値をpythonのenumモジュールを使ってあらかじめ定義しておきます。

enum.py
from enum import Enum

class SearchItem(Enum):
    USER_NAME = "userName" # UserInfoテーブルのカラム
    ORDER_ITEM = "orderItem" # Orderテーブルのカラム
    SUBSCRIPTION_DATE = "subscription_date" # Subscriptionテーブルのカラム

この3つのカラム値が検索値として使われると仮定します。

では、次にそれぞれの検索値に対してclassを定義していきます。
まずはabcモジュールを使って、baseとなる親クラスを定義します。

search_condition.py
from abc import ABCMeta, abstractmethod
from enum import SearchItem
from models import User, UserInfo, Order, Subscription

class SearchConditionBase(metaclass=ABCMeta):
    """baseとなる親クラス"""

    @abstractmethod
    def item(self) -> SearchItem:
        raise NotImplementedError()
    
    @abstractmethod
    def table(self):
        raise NotImplementedError()

    def table_join_condition(self):
        """リレーションの結合条件"""

        return User.id == self.table().user_id

次に、各検索値用のclassをSearchConditionBaseの子クラスとして定義していきます。

class UserNameSearchCondition(SearchConditionBase):
    def item(self) -> SearchItem:
        return SearchItem.USER_NAME

    def table(self):
        return UserInfo


class OrderItemSearchCondition(SearchConditionBase):
    def item(self) -> SearchItem:
        return SearchItem.ORDER_ITEM
    
    def table(self):
        return Order


class SubscriptionDateSearchCondition(SearchConditionBase):
    def item(self) -> SearchItem:
        return SearchItem.SUBSCRIPTION_DATE
    
    def table(self):
        return Subscription

そして、定義したclassのインスタンスをリストに格納します。

SEARCH_CONDITION_DEFINES: list[SearchConditionBase] = [
    UserNameSearchCondition(),
    OrderItemSearchCondition(),
    SubscriptionDateSearchCondition(),
]

さらに、格納したインスタンスを引数で指定した検索値によって動的に取り出せるように関数化します。

def find_search_condition(search_item: SearchItem) -> SearchConditionBase:
    return [con for con in SEARCH_CONDITION_DEFINES if con.item() == search_item][0]

これで、後はこの関数を呼び出し元から呼び出すと完成です!

from search_condition import find_search_condition

search_condition = find_search_condition(request.search_item)
table = search_condition.table()

query = query.join(
    table,
    search_condition.table_join_condition(),
)

終わりに

以上、今回は検索値による動的なテーブルリレーションの方法を紹介しました!
今回紹介したクラスの使い方は使いどころは多いと思うので、応用して色んな場面で使っていけたらと思います。

Discussion