| この記事の要点 |
|
どちらを使うべきか
| 方法 | 戻り値 | 向いている用途 |
|---|---|---|
Model.objects.raw() | モデルのインスタンス(RawQuerySet) | 複雑な SELECT だが、結果はモデルとして扱いたい |
connection.cursor() | タプル(行) | 集計結果、モデルに対応しない列、INSERT / UPDATE / DELETE、DB 固有の命令 |
まずは ORM(filter、annotate、Subquery など)で書けないか検討し、書けない・遅い場合に生の SQL を使うのが一般的な方針です。
モデル使用:Manager.raw()
from myapp.models import User
id = 1
users = User.objects.raw('SELECT id, name FROM user WHERE id = %s', [id])
for u in users:
print(u.id, u.name)
raw() はクエリを実行して結果をモデルのインスタンスにマッピングします。1 件だけ取りたい場合は users[0] のようにインデックスで取れますが、0 件だと IndexError になります。
raw() の注意点
- 主キー列を必ず SELECT する: 含めないと「Raw query must include the primary key」というエラーになる
- テーブル名: Django が作るテーブル名は既定で「アプリ名_モデル名の小文字」(例:
myapp_user)。Meta.db_tableを指定していない場合はUser._meta.db_tableで確認して書く - SELECT しなかったフィールドは遅延読み込みになり、アクセスした時点で追加のクエリが発行される
- モデルにない列(
COUNT(*) AS cntなど)も、別名を付ければu.cntのように属性として読める raw()の結果にfilter()などの ORM メソッドは続けて使えない
モデル未使用:connection.cursor()
from django.db import connection
id = 1
with connection.cursor() as cursor:
cursor.execute('SELECT id, name FROM user WHERE id = %s', [id])
row = cursor.fetchone() # (1, 'Taro') または None
print(row)
複数レコードを取得する場合は fetchone ではなく fetchall を使用します。
from django.db import connection
with connection.cursor() as cursor:
cursor.execute('SELECT id, name FROM user WHERE age >= %s', [20])
rows = cursor.fetchall() # [(1, 'Taro'), (2, 'Hanako'), ...]
結果を辞書で受け取る
カーソルの結果はタプルなので、列名でアクセスしたい場合は cursor.description から列名を取り出して辞書に変換します。
def dictfetchall(cursor):
columns = [col[0] for col in cursor.description]
return [dict(zip(columns, row)) for row in cursor.fetchall()]
with connection.cursor() as cursor:
cursor.execute('SELECT id, name FROM user')
users = dictfetchall(cursor) # [{'id': 1, 'name': 'Taro'}, ...]
INSERT / UPDATE / DELETE
from django.db import connection, transaction
with transaction.atomic():
with connection.cursor() as cursor:
cursor.execute('UPDATE user SET name = %s WHERE id = %s', ['Jiro', 1])
print(cursor.rowcount) # 更新された行数
Django は既定で自動コミット(autocommit)なので、execute した時点で確定します。複数の更新をまとめて成功・失敗させたい場合は transaction.atomic() で囲みます。
複数データベースを使っている場合
from django.db import connections
with connections['analytics'].cursor() as cursor:
cursor.execute('SELECT COUNT(*) FROM access_log')
total = cursor.fetchone()[0]
# raw() の場合
User.objects.using('analytics').raw('SELECT id, name FROM user')
パラメータの渡し方(SQL インジェクション対策)
値は必ず第 2 引数のリストで渡し、Django(DB ドライバ)にエスケープを任せます。
name = request.GET.get('name')
# OK: パラメータで渡す
cursor.execute('SELECT id FROM user WHERE name = %s', [name])
# NG: 文字列に埋め込む(SQL インジェクションの危険)
cursor.execute(f"SELECT id FROM user WHERE name = '{name}'")
cursor.execute("SELECT id FROM user WHERE name = '%s'" % name)
# NG: %s をクォートで囲む(値が二重にクォートされる)
cursor.execute("SELECT id FROM user WHERE name = '%s'", [name])
- プレースホルダは SQLite・PostgreSQL・MySQL いずれでも
%s(SQLite の?は使わない) - 名前付きの
%(name)sと辞書による指定は、DB バックエンドやバージョンによって対応が異なるため、リストと%sの組み合わせが最も無難 - パラメータを渡すとき、SQL 内のリテラルの
%は%%と書く必要がある。LIKE 検索は'%' + keyword + '%'をパラメータ側で組み立てるのが簡単 - テーブル名・列名はパラメータにできない。動的に変える場合は許可リストで検証してから組み立てる
keyword = 'ta'
cursor.execute('SELECT id, name FROM user WHERE name LIKE %s', ['%' + keyword + '%'])
確認方法
- 実際に発行された SQL は、
DEBUG = Trueのときconnection.queriesで確認できる python manage.py dbshellで DB に直接接続し、同じ SQL を手で実行して結果を比較するraw()の場合はprint(users.query)でパラメータを埋めた SQL を表示できる
from django.db import connection
print(connection.queries[-1]) # {'sql': 'SELECT ...', 'time': '0.001'}
関連
- SQL 副問い合わせ完全ガイド — スカラー / インライン / 相関 / EXISTS / ANY / ALL / CTE
- Django で SQL ログを出力する方法|django.db.backends と N+1 の発見
- Django Model の定義方法完全ガイド — フィールド型と Meta
- Django のテーブル名を変更する方法|アプリ名 + モデル名
子ページ
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09