Djangoのdb.models.CursorWrapper.callproc()徹底解説:ストアドプロシージャ活用の基本
簡単に言うと、DjangoのORM(Object-Relational Mapping)を介さずに、直接データベースのストアドプロシージャを実行したい場合にこのメソッドを使用します。
-
使用例
from django.db import connection def call_my_procedure(): with connection.cursor() as cursor: # 引数なしのストアドプロシージャを呼び出す場合 cursor.callproc('my_procedure_no_args') # 位置引数を持つストアドプロシージャを呼び出す場合 # 例: my_procedure_with_args(arg1, arg2) cursor.callproc('my_procedure_with_args', [10, 'hello']) # キーワード引数を持つストアドプロシージャを呼び出す場合 (データベースドライバがサポートしている場合) # 例: my_procedure_with_kwargs(name='John', age=30) # 全てのデータベースがキーワード引数をサポートするわけではない点に注意 # cursor.callproc('my_procedure_with_kwargs', kparams={'name': 'John', 'age': 30}) # ストアドプロシージャの結果を取得する場合 (ストアドプロシージャが結果セットを返す場合) rows = cursor.fetchall() for row in rows: print(row)
-
何ができるのか?
callproc()
を使うと、以下のようなことが可能になります。- 既存のストアドプロシージャの再利用
既にデータベースに定義されている複雑なロジックを持つストアドプロシージャを、Djangoアプリケーションから簡単に呼び出すことができます。 - パフォーマンスの向上
複雑な処理をストアドプロシージャとしてデータベース側で実行することで、アプリケーションとデータベース間のデータ転送量を減らし、パフォーマンスを向上させることができます。 - トランザクション管理の簡素化
ストアドプロシージャ内で複数のデータベース操作を一つのトランザクションとして実行できるため、アプリケーション側でのトランザクション管理が簡素化される場合があります。
- 既存のストアドプロシージャの再利用
-
callproc(procname, params=None, kparams=None)の引数
procname
: 呼び出したいストアドプロシージャの名前を文字列で指定します。params
: 位置引数(positional arguments)としてストアドプロシージャに渡すパラメータのシーケンス(リストやタプルなど)を指定します。kparams
: キーワード引数(keyword arguments)としてストアドプロシージャに渡すパラメータの辞書を指定します。
-
CursorWrapper
とは?CursorWrapper
は、Djangoがデータベースとやり取りする際に使用するカーソルオブジェクトをラップ(包装)したものです。データベースドライバが提供する生のカーソルオブジェクトに、Django独自の機能やエラーハンドリングなどを追加しています。
よくあるエラーとトラブルシューティング
-
- 原因
使用しているデータベースドライバ(例:psycopg2
for PostgreSQL,mysqlclient
for MySQL,cx_Oracle
for Oracle,pyodbc
for SQL Serverなど)が、callproc()
メソッドをサポートしていないか、あるいは異なる名前で提供している可能性があります。特にpyodbc
を使用している場合、callproc
が実装されていないことが知られています。 - トラブルシューティング
- データベースドライバのドキュメントを確認する
使用しているデータベースドライバの公式ドキュメントで、ストアドプロシージャを呼び出すための正しい方法を確認してください。多くの場合、cursor.execute()
を使ってCALL my_procedure(arg1, arg2)
のようなSQL文を実行する必要があります。 - 代替手段の検討
callproc()
が利用できない場合は、cursor.execute()
を使用してストアドプロシージャを呼び出すようにコードを書き換える必要があります。
- データベースドライバのドキュメントを確認する
- 原因
-
ストアドプロシージャが見つからない、または名前が間違っている
- 原因
callproc()
に渡されたストアドプロシージャ名が、データベースに存在する実際の名前と一致しない。- スキーマ名やパッケージ名を含める必要があるが、含めていない。
- 大文字・小文字の区別が間違っている(特にPostgreSQLなどでは大文字・小文字を区別する場合があります)。
- トラブルシューティング
- データベースで名前を確認する
データベースクライアント(pgAdmin, MySQL Workbench, SQL Developerなど)を使用して、ストアドプロシージャの正確な名前とスキーマ(またはパッケージ)を確認します。 - 大文字・小文字の区別
データベースが名前の大文字・小文字を区別する場合、Pythonコードでも正確に大文字・小文字を合わせる必要があります。
- データベースで名前を確認する
- 原因
-
引数の不一致(数、型)
- 原因
callproc()
に渡された引数の数が、ストアドプロシージャが期待する引数の数と異なる。- 引数のデータ型が、ストアドプロシージャが期待するデータ型と互換性がない。
- 出力引数(OUT/INOUTパラメータ)の扱いが間違っている。
- トラブルシューティング
- ストアドプロシージャの定義を確認する
データベースでストアドプロシージャの定義(引数の数、順序、データ型)を正確に確認します。 - Python側のデータ型を合わせる
Pythonのデータ型を、データベースのデータ型に合うように変換します。例えば、データベースの整数型にはPythonのint
、文字列型にはstr
を渡します。 - 出力引数のハンドリング
ストアドプロシージャが出力引数(OUT/INOUTパラメータ)を持つ場合、データベースドライバによってその取得方法が異なります。一般的には、callproc()
の戻り値や、カーソルオブジェクトの特定のメソッド(例: Oracleのvar
オブジェクト)を使用して取得します。各データベースドライバのドキュメントを確認してください。
- ストアドプロシージャの定義を確認する
- 原因
-
トランザクション関連のエラー
- 原因
callproc()
を実行した後にconnection.commit()
またはconnection.rollback()
が適切に呼び出されていない。- ストアドプロシージャ自体がトランザクションを内部で管理しているが、Djangoのトランザクション管理と競合している。
- トラブルシューティング
-
with connection.cursor() as cursor:を使用する
これにより、カーソルが自動的に閉じられ、トランザクションが管理しやすくなります。 -
明示的なコミット/ロールバック
callproc()
で変更を加えるストアドプロシージャを呼び出した場合、connection.commit()
を明示的に呼び出す必要があります。エラーが発生した場合はconnection.rollback()
を呼び出します。 -
Djangoのtransaction.atomic()と組み合わせる
ストアドプロシージャの呼び出しを含む一連の処理を、Djangoのtransaction.atomic()
ブロックで囲むことで、Djangoのトランザクション管理に含めることができます。from django.db import connection, transaction def call_and_commit_procedure(): with transaction.atomic(): # これによりトランザクションが管理される with connection.cursor() as cursor: cursor.callproc('my_procedure_that_modifies_data')
-
ストアドプロシージャのトランザクション動作の確認
ストアドプロシージャ自体がコミットやロールバックを行っていないか確認します。場合によっては、アプリケーション側でトランザクションを管理する必要があるため、ストアドプロシージャ内のトランザクションは削除するか、注意して使用する必要があります。
-
- 原因
-
MySQL commands out of sync; you can't run this command now
(MySQL)- 原因
MySQL特有のエラーで、ストアドプロシージャが複数の結果セットを返したり、適切に結果セットが消費されていない場合に発生します。 - トラブルシューティング
- cursor.fetchall()またはcursor.fetchone()で結果セットを消費する
ストアドプロシージャが何らかの結果セットを返している場合、たとえそれが空であっても、cursor.fetchall()
などで結果セットを完全に消費する必要があります。 - cursor.nextset()を使用する
複数の結果セットを返すストアドプロシージャの場合、各結果セットを処理するためにcursor.nextset()
を繰り返し呼び出す必要がある場合があります。 - ストアドプロシージャの修正
ストアドプロシージャが不要な結果セットを返さないように修正することも検討します。
- cursor.fetchall()またはcursor.fetchone()で結果セットを消費する
- 原因
- ロギングを有効にする
Djangoのデータベースクエリロギングを有効にすることで、callproc()
が実際にどのようなクエリを生成しているか、あるいはどのようなエラーが発生しているかを詳しく知ることができます。-
settings.py
で以下を設定します。LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': {
-
- エラーメッセージをよく読む
Pythonのトレースバックやデータベースのエラーメッセージには、問題解決の手がかりが隠されています。 - 生のSQLでテストする
まず、データベースクライアント(またはcursor.execute()
)を使って、ストアドプロシージャが正しく実行できることを確認します。これにより、問題がストアドプロシージャ自体にあるのか、Djangoのコードにあるのかを切り分けることができます。
'handlers': ['console'], 'level': 'DEBUG', # DEBUGレベルで詳細なSQLログが出力される 'propagate': False, }, }, } ```
- 公式ドキュメントを参照する
Djangoの公式ドキュメントの「Performing raw SQL queries」セクションや、使用しているデータベースドライバの公式ドキュメントは、非常に貴重な情報源です。 - データベースドライバのバージョンを確認する
使用しているデータベースドライバ(psycopg2
など)とデータベースサーバー、Pythonのバージョン間の互換性を確認してください。
callproc()
は、データベースのストアドプロシージャを呼び出すためのメソッドです。ORM(Object-Relational Mapping)を介さずに、直接データベースとやり取りしたい場合に使用します。
前提
- 使用するデータベース(PostgreSQL, MySQL, Oracleなど)に、事前にストアドプロシージャが作成されていること。
- Djangoプロジェクトがセットアップされており、データベースが設定済みであること。
例1: 引数なしのストアドプロシージャの呼び出し
最も基本的なケースで、ストアドプロシージャが何の引数も取らず、結果も返さない場合です。
データベース側の準備 (PostgreSQLの例)
-- employeesというテーブルがあると仮定
-- CREATE TABLE employees (
-- id SERIAL PRIMARY KEY,
-- name VARCHAR(100),
-- hire_date DATE
-- );
-- テスト用のストアドプロシージャ
CREATE OR REPLACE PROCEDURE log_employee_access()
LANGUAGE plpgsql
AS $$
BEGIN
-- ここで何らかのロギング処理を行う (例: ログテーブルに挿入)
-- RAISE NOTICE '従業員データにアクセスがありました。'; -- デモンストレーション用
END;
$$;
Djangoでの呼び出し
from django.db import connection
def call_no_arg_procedure():
with connection.cursor() as cursor:
try:
print("ストアドプロシージャ 'log_employee_access' を呼び出します...")
cursor.callproc('log_employee_access')
# ストアドプロシージャがCOMMITを含む場合は不要ですが、
# DDLやDMLを実行する場合は明示的なコミットが必要な場合があります。
# connection.commit()
print("ストアドプロシージャの呼び出しが完了しました。")
except Exception as e:
print(f"エラーが発生しました: {e}")
# connection.rollback() # エラー時はロールバック
例2: 引数を渡し、結果セットを返すストアドプロシージャの呼び出し
ストアドプロシージャに引数を渡し、結果としてテーブルデータのような結果セット(複数行)を返す場合です。
データベース側の準備 (PostgreSQLの例)
-- employeesテーブルにいくつかデータを挿入しておきます
-- INSERT INTO employees (name, hire_date) VALUES ('Alice', '2020-01-15'), ('Bob', '2021-03-20'), ('Charlie', '2019-11-01');
-- 入力引数 (minimum_hire_year) を受け取り、それ以降に採用された従業員を返すストアドプロシージャ
CREATE OR REPLACE FUNCTION get_employees_hired_after_year(minimum_hire_year INT)
RETURNS TABLE (
id INT,
name VARCHAR(100),
hire_date DATE
)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY SELECT e.id, e.name, e.hire_date
FROM employees e
WHERE EXTRACT(YEAR FROM e.hire_date) >= minimum_hire_year;
END;
$$;
Djangoでの呼び出し
from django.db import connection
def get_employees_by_hire_year(year):
with connection.cursor() as cursor:
try:
print(f"ストアドプロシージャ 'get_employees_hired_after_year' を呼び出します (年: {year})...")
# 引数はリストまたはタプルで渡します
cursor.callproc('get_employees_hired_after_year', [year])
# 結果セットを取得します
columns = [col[0] for col in cursor.description] # カラム名を取得
rows = cursor.fetchall()
print(f"取得した従業員データ ({len(rows)}件):")
for row in rows:
# タプル形式で返されるので、カラム名とマッピング
employee_data = dict(zip(columns, row))
print(employee_data)
return rows
except Exception as e:
print(f"エラーが発生しました: {e}")
return None
# 使用例: 2021年以降に採用された従業員を取得
# get_employees_by_hire_year(2021)
ポイント
cursor.fetchall()
はすべての結果行を取得し、cursor.fetchone()
は1行ずつ取得します。cursor.description
を使用して、結果セットのカラム名を取得できます。これは、結果のタプルをより読みやすい辞書形式に変換するのに役立ちます。
データベースの種類やドライバによって、出力引数の扱いは異なります。ここでは、PostgreSQLのFUNCTION
の戻り値、または一般的なデータベースドライバの慣例について説明します。
PostgreSQLの場合 (FUNCTIONの戻り値として扱う)
PostgreSQLでは、OUT
パラメータを持つPROCEDURE
もありますが、単一の値を返す場合はFUNCTION
を使うのが一般的です。
データベース側の準備 (PostgreSQLの例)
-- 特定の年に入社した従業員の数を返す関数
CREATE OR REPLACE FUNCTION count_employees_hired_in_year(target_year INT)
RETURNS INT
LANGUAGE plpgsql
AS $$
DECLARE
employee_count INT;
BEGIN
SELECT COUNT(*) INTO employee_count
FROM employees
WHERE EXTRACT(YEAR FROM hire_date) = target_year;
RETURN employee_count;
END;
$$;
Djangoでの呼び出し (PostgreSQL Functionの場合)
PostgreSQLのFUNCTION
は、通常のSELECT
文のように実行し、結果をWorkspaceone()
などで取得するのが一般的です。callproc
はPROCEDURE
向けですが、FUNCTION
もcallproc
で呼び出せる場合があります。しかし、より一般的なのはexecute
です。
execute()を使ったPostgreSQL FUNCTIONの呼び出し (推奨される方法)
from django.db import connection
def count_employees_in_year_function(year):
with connection.cursor() as cursor:
try:
print(f"関数 'count_employees_hired_in_year' を呼び出します (年: {year})...")
# 関数はSELECT文のように実行
cursor.execute("SELECT count_employees_hired_in_year(%s);", [year])
result = cursor.fetchone()
if result:
count = result[0]
print(f"{year}年に採用された従業員の数: {count}人")
return count
return 0
except Exception as e:
print(f"エラーが発生しました: {e}")
return None
# 使用例: 2020年に採用された従業員の数を取得
# count_employees_in_year_function(2020)
callproc()
を使った一般的な出力引数の取得(例: OracleのOUTパラメータ、MySQLのINOUTパラメータ)
これはデータベースドライバとデータベースに大きく依存しますが、概念としては以下のようになります。
データベース側の準備 (概念 - MySQLの例)
DELIMITER //
CREATE PROCEDURE get_employee_count_and_name(
IN employee_id INT,
OUT employee_name VARCHAR(100),
OUT employee_count INT
)
BEGIN
SELECT name INTO employee_name FROM employees WHERE id = employee_id;
SELECT COUNT(*) INTO employee_count FROM employees;
END //
DELIMITER ;
Djangoでの呼び出し (概念 - 各ドライバのドキュメント参照が必須)
from django.db import connection
def get_employee_details(emp_id):
with connection.cursor() as cursor:
try:
# データベースドライバによっては、OUTパラメータのプレースホルダにNoneなどを渡す場合があります。
# 例えば、Oracleのcx_Oracleでは、varオブジェクトを事前に作成して渡す必要があります。
# MySQLのmysqlclientでは、OUT/INOUTパラメータは直接リストに追加します。
# しかし、callproc()の戻り値としてOUTパラメータが返されることも多いです。
# ここは一般的な例として、戻り値がOUTパラメータに対応すると仮定
# MySQLの例:
# params = [emp_id, None, None] # OUTパラメータのプレースホルダ
# result = cursor.callproc('get_employee_count_and_name', params)
# 多くのデータベースドライバでは、callproc()実行後に
# cursor.fetchall()で結果セット(もしあれば)を取得し、
# OUTパラメータはcallproc()の戻り値としてタプルで返されるか、
# paramsリストが更新される形で返されることがあります。
# 例えば、MySQLのconnector/pythonでは、paramsリストが更新されます。
# result = cursor.callproc('get_employee_count_and_name', [emp_id, 0, '']) # INOUTパラメータの初期値
# employee_name = result[1] # もし戻り値がOUTパラメータのタプルなら
# employee_count = result[2]
# 最も一般的な方法は、ストアドプロシージャ内で結果セットを返すか、
# あるいはexecuteでSELECT @out_param; のようにセッション変数で取得する方法です。
# callproc()で直接OUTパラメータを取得するロジックは、ドライバによって非常に異なります。
print("注意: 出力引数の扱いはデータベースドライバに大きく依存します。")
print(" 使用しているデータベースドライバの公式ドキュメントを参照してください。")
# 代替案: データベースドライバでOUTパラメータを扱う場合
# MySQLの例 (cursor.execute()を使用):
# cursor.execute("CALL get_employee_count_and_name(%s, @emp_name, @emp_count);", [emp_id])
# cursor.execute("SELECT @emp_name, @emp_count;")
# result = cursor.fetchone()
# if result:
# name, count = result
# print(f"従業員ID {emp_id} の名前: {name}, 総従業員数: {count}")
# return name, count
except Exception as e:
print(f"エラーが発生しました: {e}")
return None
# get_employee_details(1)
- トランザクション
ストアドプロシージャがデータベースの状態を変更する場合(INSERT, UPDATE, DELETEなど)、connection.commit()
を明示的に呼び出すか、Djangoのトランザクション管理(transaction.atomic()
など)を使用する必要があります。 - cursor.execute() vs callproc()
データベースによっては、callproc()
が期待通りに機能しない場合や、FUNCTION
の呼び出しにはcursor.execute()
でSELECT my_function(...)
のように実行するのが適切である場合があります。 - データベースとドライバの依存性
callproc()
の振る舞い(特に引数の渡し方や出力引数の取得方法)は、使用しているデータベース(PostgreSQL, MySQL, Oracle, SQL Serverなど)と、それに使用するPythonのデータベースドライバ(psycopg2
,mysqlclient
,cx_Oracle
,pyodbc
など)に大きく依存します。上記の例は一般的な概念を示すものであり、実際のコードを記述する際は、該当するデータベースとドライバの公式ドキュメントを必ず参照してください。
callproc()
は低レベルな操作であり、通常はORMではカバーできない特殊なケースやパフォーマンス最適化のために利用されますが、多くの場合、以下の代替手段で同等またはそれ以上の目的を達成できます。
cursor.execute() を使った生のSQLの実行 (最も一般的)
callproc()
がストアドプロシージャの呼び出しに特化しているのに対し、cursor.execute()
はあらゆる種類の生のSQL文を実行できる、より汎用的なメソッドです。ストアドプロシージャの呼び出しもこれで行うことができます。
特徴
- セキュリティ
パラメータのプレースホルダ(%s
や?
など)を適切に使うことで、SQLインジェクションのリスクを回避できます。 - より詳細な制御
SQL文を直接記述するため、細かなチューニングや、データベース固有の構文を利用できます。 - ストアドプロシージャ/関数の呼び出し
多くのデータベースでは、ストアドプロシージャや関数をCALL procedure_name(...)
やSELECT function_name(...)
のように実行します。 - 汎用性
SELECT
,INSERT
,UPDATE
,DELETE
などのDML文だけでなく、CREATE TABLE
,ALTER TABLE
などのDDL文も実行できます。
使用例
from django.db import connection, transaction
def call_procedure_with_execute(employee_id):
with connection.cursor() as cursor:
try:
# ストアドプロシージャの呼び出し (MySQL/PostgreSQLの場合)
# MySQL: CALL get_employee_details(%s);
# PostgreSQL: SELECT * FROM get_employee_details(%s); (関数として定義されている場合)
# PostgreSQL: CALL get_employee_details(%s); (プロシージャとして定義されている場合)
cursor.execute("CALL get_employee_details(%s);", [employee_id])
# 結果セットがある場合、fetchall()などで取得
# columns = [col[0] for col in cursor.description]
# rows = cursor.fetchall()
# for row in rows:
# print(dict(zip(columns, row)))
print(f"ストアドプロシージャを execute() で呼び出しました (ID: {employee_id})。")
# 変更を伴う操作の場合、明示的にコミット
# connection.commit()
# または transaction.atomic() を使う
except Exception as e:
print(f"エラーが発生しました: {e}")
# connection.rollback()
raise
def run_raw_select_query():
with connection.cursor() as cursor:
cursor.execute("SELECT id, name FROM my_app_employee WHERE age > %s;", [30])
rows = cursor.fetchall()
for row in rows:
print(f"ID: {row[0]}, Name: {row[1]}")
callproc()とexecute()の使い分け
- execute()
より汎用的で、ストアドプロシージャ/関数呼び出しだけでなく、あらゆるSQL文を実行できます。データベースドライバ間の挙動の差異も比較的少ないため、多くのケースで推奨される代替手段です。 - callproc()
ストアドプロシージャの呼び出しに特化しており、一部のデータベースドライバでは引数のIN/OUT/INOUTパラメータの処理をよりシンプルにする場合があります。しかし、ドライバによる挙動の差異が大きいです。
Django ORMの機能で代替できないか検討する
ストアドプロシージャを呼び出そうとしている処理が、実はDjangoのORM(QuerySet API)で実現できる場合があります。可能な限りORMを使用することが、Djangoの推奨されるプラクティスです。
特徴
- 自動化
マイグレーション、テスト、管理画面との連携などが容易です。 - 可読性/保守性
Pythonオブジェクトとしてデータを扱えるため、コードの可読性が高く、保守が容易です。 - 安全性
SQLインジェクションなどのセキュリティリスクを自動的に管理してくれます。 - 抽象化
データベースの種類を意識せずにコードを書けるため、DBの切り替えが容易です。
使用例
-
データの挿入/更新/削除
# 挿入 new_employee = Employee.objects.create(name='David', hire_date='2024-05-15') # 更新 Employee.objects.filter(name='Alice').update(hire_date='2020-01-01') # 削除 Employee.objects.filter(name='Bob').delete()
-
集計処理
from django.db.models import Count # ストアドプロシージャでやっていた「特定の年に入社した従業員の数を数える」 employee_count = Employee.objects.filter(hire_date__year=2020).count() # または、より複雑な集計 department_employee_counts = Employee.objects.values('department__name').annotate(total_employees=Count('id'))
-
from my_app.models import Employee # ストアドプロシージャでやっていた「特定の年以降に採用された従業員を取得」 employees = Employee.objects.filter(hire_date__year__gte=2021) for emp in employees: print(f"{emp.name} - {emp.hire_date}")
いつORMを使うべきか?
- データベース間の互換性を保ちたい場合。
- 複雑な結合や集計もORMの
select_related()
,prefetch_related()
,annotate()
,aggregate()
などで対応できることが多いです。 - CRUD(作成、読み取り、更新、削除)操作のほとんどはORMで可能です。
ORMの機能だけでは不十分だが、生のSQLクエリの結果をORMオブジェクトにマッピングしたい場合に便利です。cursor.execute()
で直接SQLを実行するよりも、結果を扱いやすくなります。
特徴
- 生のSQLを記述するため、データベース固有の機能を利用できます。
SELECT
クエリにのみ適用可能です(DML/DDLは実行できません)。- 生のSQLクエリを実行し、その結果をモデルインスタンスとして取得できます。
使用例
from my_app.models import Employee
def get_employees_with_raw_query(min_year):
# ストアドプロシージャ(関数)で取得したデータをモデルにマッピングする例
# ここでは、直接生のSQLでデータを取得する例を示す
query = "SELECT id, name, hire_date FROM my_app_employee WHERE EXTRACT(YEAR FROM hire_date) >= %s"
employees = Employee.objects.raw(query, [min_year]) # パラメータも安全に渡せる
for emp in employees:
# emp は Employee モデルのインスタンスとして振る舞う
print(f"ID: {emp.id}, Name: {emp.name}, Hire Date: {emp.hire_date}")
RawQuerySetを使うべきシナリオ
- ストアドプロシージャが結果セットを返し、その結果を既存のモデルにマッピングしたい場合。
- データベース固有の最適化されたSQL関数や構文を使用したいが、結果はモデルインスタンスとして扱いたい場合。
- 非常に複雑な
SELECT
クエリで、ORMのQuerySet APIでは記述が困難な場合。
方法 | 特徴 | 用途 |
---|---|---|
cursor.execute() | あらゆる生のSQLを実行。最も低レベルで強力。DBドライバに依存。 | DDL/DML操作、複雑なクエリ、ストアドプロシージャ/関数の呼び出し(多くの場合これが最良の代替)。 |
Django ORM | PythonオブジェクトとしてDBを操作。DBの種類を抽象化。安全性、可読性。 | ほとんどのCRUD操作、一般的な集計、リレーション。 |
RawQuerySet | 生のSQLクエリ結果をモデルインスタンスにマッピング。SELECT 専用。 | ORMでは難しいがモデルにマッピングしたい複雑なSELECTクエリ。 |