0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Django】ORMでSQLを使う - Django初心者

Last updated at Posted at 2024-11-24

はじめに

元々現場ではJavaなどの言語を触っていたこともあり、SQLを高頻度で書いていました。

ですがDjangoを触り始めてORMでのクエリ発行になかなか詰まったので、備忘録としてまとめてみました。
 

当記事では以下の構成でまとめていきます。

1.こんなSQLを実行したい

2.Djangoでの書き方(解説・補足等)

環境

動作確認は以下の環境で行いました。

  • MySQL

■SELECT

・データの全件取得

SQL
SELECT A.*
FROM my_modles A
python
from models import MyModel

many_data = MyModel.objects.all()

補足:

  • all()を指定することで全件取得が可能

・指定カラムの取得

SQL
SELECT A.col1, A.col2....
FROM my_modles A
python
from models import MyModel

many_data = MyModel.objects.values("col1","col2")
# または
many_data = MyModel.objects.values_list("col1","col2")
# または
many_data = MyModel.objects.only("col1","col2")

補足:

  • values() → 値を辞書型のリストで返却する
・values()の戻り値
[
    {'col1': 'value1_1', 'col2': 'value1_2'},
    {'col1': 'value2_1', 'col2': 'value2_2'},
    ...
]

 

  • values_list() → 値をタプル型のリストで返却する
・values_lsit()の戻り値
[
    ('value1_1', 'value1_2'),
    ('value2_1', 'value2_2'),
    ...
]

values_listflatオプションを持っており単一カラムの取得時に便利

values_listのflatオプション
many_data = MyModel.objects.values_list("col1", flat=True).all()

print(many_data)
# タプルではなくリスト形式になり、データへのシンプルなアクセスが可能
# => ['value1', 'value2', 'value3', ...]
  • only() → 値をモデルのインスタンスで返却する
・values_lsit()の戻り値
MyModel Instance: <MyModel: MyModel object (1)>, Value1: test_val, Value2: 500.00
MyModel Instance: <MyModel: MyModel object (2)>, Value1: test_val2, Value2: 200.00

■FROM

■前提
JOINに関してはrelationがmodel設定されていることがとても重要になります。

Djangoでは関連先のオブジェクトを取得し、クエリ発行回数を減らすことでパフォーマンスの向上につながります。

・INNER JOIN

SQL
SELECT A.*, B.*
FROM my_modles A
INNER JOIN other_models B
ON A.pk = B.my_models_id
python
from models import MyModel
from models import OtherModel

many_data = MyModel.objects.select_related("other_model").all()

補足:

  • select_relatedを使用することでINNER JOINが可能になる
     ○仕様
        - 対一の関係性を持つテーブルを取得する
          → 多対一(ForeignField)、一対一(OneToOneField
python
# 関連先のオブジェクトデータを取得する
many_data = MyModel.objects.select_related("other_model").all()
other_data = many_data.other_model

・LEFT JOIN

SQL
SELECT A.*, B.*
FROM my_modles A
LEFT OUTER JOIN other_models B
ON A.pk = B.my_models_id
python
from models import MyModel
from models import OtherModel

many_data = MyModel.objects.prefetch_related("other_model").all()

補足:

 ○仕様
    - 対多の関係性を持つテーブルを取得する
      → 多対多(ManyToManyField

※厳密には異なる箇所がありますが割愛させてください。

■WHERE

・取得件数

SQL
SELECT *
FROM my_modles A
WHERE A.hoge_col = "hoge"
python
from models import MyModel

# 複数件取得
many_data = MyModel.objects.filter(hoge_col="hoge")

# 1件取得
many_data = MyModel.objects.filter(hoge_col="hoge").first()
#または
many_data = MyModel.objects.get(pk=1)

補足:

  • filtergetの違いと特徴
    get
     - 単一のオブジェクトを返却する。
     - 取得件数が0件の場合DoesNotExistエラーが発生する

filter
 - 複数のオブジェクトを返却する。
 - 取得件数が0件でもエラーが発生しない。
 - 取得オブジェクトに対し.queryを使用し、発行されるクエリを確認できる

・演算子

⚪︎AND/OR

■前提
Djangoではデフォルトの論理演算子はANDが使用されてしまう。

論理演算子を容易に実装可能にしてくれるのが、Qオブジェクト
である

SQL
SELECT *
FROM my_modles A
WHERE
    (A.hoge_col1 = "hoge" and not A.hoge_col2 = "huga")
    or A.hoge_col3 = "piyo"
    
python
from django.db.models import Q
from models import MyModel

many_data = MyModel.objects.filter(
    ( Q(hoge_col1="hoge") & ~Q(hoge_col2="huga") ) 
    | Q(hoge_col3="piyo")
    
)

補足:

  • AND& 記号を用いる
  • OR|(パイプ) 記号を用いる
  • NOTの場合 は ~ 記号をQの前に用いる

⚪︎比較

SQL
SELECT *
FROM my_modles A
WHERE
    A.number1 > hoge
    or A.number2 >= 3
    or A.number3 < 3
    or A.number4 <= 3
python
from django.db.models import Q
from models import MyModel

many_data = MyModel.objects.filter(
    Q(number1__gt=3) 
    | Q(number2__gte=3) 
    | Q(number3__lt=3) 
    | Q(number4__lte=3) 
)

補足:

  • __(アンダースコア2つ)が必要

⚪︎LIKE句

SQL
SELECT *
FROM my_modles A
WHERE
    A.hoge_col1 LIKE '%hoge'
    OR A.hoge_col2 LIKE 'hoge%'
    OR A.hoge_col3 LIKE '%hoge%'
python
from django.db.models import Q
from models import MyModel

many_data = MyModel.objects.filter(
     Q(hoge_col1__startwith="hoge")
     | Q(hoge_col2__endwith="hoge")
     | Q(hoge_col3__contains="hoge")
)

補足:

  • 前方一致 → startwith
  • 後方一致 → endwith
  • 部分一致 → contains
  • それぞれの頭にiを付与することで大文字、小文字の区別を行わなくなる(例:istartwith

⚪︎IN句

SQL
SELECT *
FROM my_modles A
WHERE
    A.hoge_col in (1,3)
python
from models import MyModel

list = [1,3]
many_data = MyModel.objects.filter(hoge_col__in=list)

補足:

  • __(アンダースコア2つ)が必要
  • 検索したい値をlistまたはtupleで渡す

⚪︎BETWEEN句

SQL
SELECT *
FROM my_modles A
WHERE
    A.hoge_col BETWEEN 1 and 10
python
from models import MyModel

list = [1,3]
many_data = MyModel.objects.filter(A.hoge_col__range=(1, 10))

補足:

  • __(アンダースコア2つ)が必要

■GROUP BY

SQL
SELECT A.id, A.name, SUM(A.hoge_num) as sum_data
FROM my_modles A
GROUP BY A.id, A.name
python
from django.db.models import Sum
from models import MyModel

many_data = MyModel.objects.annotate(sum_data=Sum("hoge_col")).only("id", "name", "sum_data")

補足:

  • annotateを用いて集計を行うカラムを用意
  • 内部的にGROUP_BYしてくれるが、必要に応じてonly()メソッドを利用して任意のGROUP BYが可能になる

■HAVING

SQL
SELECT A.id, A.name, SUM(A.hoge_col) as sum_data
FROM my_modles A
WHERE name LIKE '%hoge%'
GROUP BY A.id, A.name
HAVING sum_data > 5
python
from django.db.models import Sum
from models import MyModel

many_data = MyModel.objects
    .filter(name__contains="hoge")
    .annotate(sum_data=Sum("hoge_col"))
    .filter(sum_data__gt=5)

補足:

  • annotateを用いたのちにfilterHAVINGの指定が可能
  • WHEREHAVINGを一つのfilterで指定できない

■ORDER BY

SQL
SELECT A.*
FROM my_modles A
ORDER BY A.hoge_col asc
--ORDER BY A.hoge_col desc
python
from models import MyModel

asc_data = MyModel.objects.all().order_by("hoge_col")
desc_data = MyModel.objects.all().order_by("-hoge_col")

補足:

  • desc(降順)の場合から無名の前に-をつける

■その他関数

・COUNT

SQL
SELECT A.col1, A.col2, Count(A.col3) as count_col
FROM my_modles A
python
from django.db.models import Count
from models import MyModel

many_data = MyModel.objects.annotate(count_col=Count("col3"))

・SUM

SQL
SELECT A.col1, A.col2, Sum(A.col3) as sum_col
FROM my_modles A
python
from django.db.models import Sum
from models import MyModel

many_data = MyModel.objects.annotate(sum_col=Sum("col3"))

CASE/WHEN

SQL
SELECT
 A.col1
 , A.col2
 , CASE col3 
    WHEN 1 THEN "one" 
    WHEN 2 THEN "two" 
    WHEN 3 THEN "three" 
    ELSE "other"
 END as case_col
FROM my_modles A
python
from django.db.models import Case, When
from models import MyModel

many_data = MyModel.objects.annotate(
     case_col=Case(
         When(col3=1, then=Value("one"))
        ,When(col3=2, then=Value("two"))
        ,When(col3=3, then=Value("three"))
        ,default=Value("other")
        ,output_field=models.CharField()
     )

補足:

  • Case内にWhenを引数として渡し使用する
  • defaultELSEの役割を担う
  • output_fieldは戻り値の型を明示的に示す

■INSERT

更新中・・・

■UPDATE

更新中・・・

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?