utamaro’s blog

誰かの役に立つ情報を発信するブログ

DjangoのORMを使わないでクエリを実行する方法

ORM(object relational mapper)を使うべきか、使わざるべきかの議論を時々目にします。

Author.objects.all()←こういうやつです。

今回は、DjangoでORMを使わずにクエリを書いてDBからデータを取得する方法を紹介します。

クエリの生成にはjinjasqlというpythonのライブラリを使用します。

ライブラリは↓のようにインストールが可能です。

pip install jinjasql

jinjasqlはjinja2というテンプレートエンジンを基にしているので、柔軟にクエリを書くことができます。

github.com

紹介

まずはModelについてです。

class Data(models.Model):

    title = models.TextField(max_length=400)
    is_deleted = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'card'

    def select_data_list(self, limit):
        template, data = select_data_list_template(limit)
        query, bind_params = j.prepare_query(template, data)
        datas = Data.objects.raw(query, bind_params)
        return list(datas)

このモデル(Data)を使って、dataテーブルからデータを取得します。

select_data_listメソッドの中身に注目してください。

template, data = select_data_list_template(limit)  # 1
query, bind_params = j.prepare_query(template, data)  # 2
datas = Data.objects.raw(query, bind_params)  # 3

1でクエリのもととなる文字列と、データを用意します。

1の関数は↓のように作成します。

def select_data_list_template(limit):
    template = """
    select
        id,
        title,
        is_deleted,
        created_at
    from
        data
    order by created_at desc
    limit {{limit}}
    """
    data = {
        'limit': limit,
    }
    return template, data

jinjasqlではjinja2と同じようにfor文を使ったり、if文を使ったりできます。

例ではlimitの指定しかしていませんが、「ある値の場合には条件1を、別の値の場合は条件2をwhereに設定する」といったことができます。

これはspringというjavaフレームワークで使えるmybatisと同じようなことができるということです。

次に、2でjinjasqlというライブラリを使ってDjangoで使えるクエリを生成します。

queryには↓の文字列が作成されます。

select
    id,
    title,
    is_deleted,
    created_at
from
    data
order by card.created_at desc
limit %s

bind_paramsには↓のオブジェクトが作成されます。

odict_values([30])

最後に、3でquerybind_paramsDjangoで用意されているraw関数を使って実行します。

取得されるデータはlist化する必要があるので少し面倒ですが、複雑なクエリをORMを使って書くよりは面倒ではないと思います。