Django逆引きチートシート（QuerySet編）

コード例に出てくる`Product`、`Order`、`OrderDetail`はモデルクラス

## 検索系

### 全てのレコードを取得したい

```python
products = Product.objects.all()
```

### 主キーを指定してレコードを取得したい
主キーのカラム名がなんであっても`pk=`で検索できる

```python
product = Product.objects.get(pk=1)
```

### 取得条件を指定してレコードを取得したい

```python
products = Product.objects.filter(name="hoge")
```

### 取得条件を複数(AND)指定してレコードを取得したい
カンマ区切りで複数かくとANDになる

```python
products = Product.objects.filter(name="hoge", size="L")
```

### 取得条件を複数(IN)指定してレコードを取得したい
`<カラム名>=`の代わりに`<カラム名>__in=`とする

```python
products = Product.objects.filter(name__in=["hoge", "fuga"])
```

### 取得条件を複数(OR)指定してレコードを取得したい

`Q(<条件>)`で一つの条件が生成できる。この式を`|`で繋ぐと`OR`になる。

```python
from django.db.models import Q

products = Product.objects.filter(Q(name="hoge") | Q(name__startswith="foo"))
# -> name='hoge' OR name LIKE 'foo%'
```

### 大小比較の取得条件でレコードを取得したい

```python
# weight > 2
products = Product.objects.filter(weight__gt=2)
# weight < 2
products = Product.objects.filter(weight__lt=2)
# weight >= 2
products = Product.objects.filter(weight__gte=2)
# weight <= 2
products = Product.objects.filter(weight__lte=2)
```

### 範囲条件でレコードを取得したい

```python
# 4から12まで
# (weight BETWEEN 4 AND 12)
products = Product.objects.filter(weight__range=(4, 12))
```

### SQLの`IS NULL`（または`IS NOT NULL`）検索をしたい

```python
# size IS NULL
products = Product.objects.filter(size__isnull=True)
# size IS NOT NULL
products = Product.objects.filter(size__isnull=False)
```

### 前方一致（LIKE）でレコードを取得したい
`<カラム名>=`の代わりに`<カラム名>__startswith=`とする

```python
products = Product.objects.filter(name__startswith="ho")
```

### 中間一致（LIKE）でレコードを取得したい
`<カラム名>=`の代わりに`<カラム名>__contains=`とする

```python
products = Product.objects.filter(name__contains="ho")
```

### 後方一致（LIKE）でレコードを取得したい
`<カラム名>=`の代わりに`<カラム名>__endswith=`とする

```python
products = Product.objects.filter(name__endswith="ho")
```

### 昇順でソートして取得したい
`name`の昇順

```python
products = Product.objects.order_by("name")
```

`filter()`がある場合は`filter()`の後に続けて書く

### 降順でソートして取得したい
`name`の降順

```python
products = Product.objects.order_by("-name")
```

`filter()`がある場合は`filter()`の後に続けて書く

### 重複除去(SELECT DISTINCT)して取得したい

```python
products = Product.objects.distinct("name", "created_on")
```

`filter()`がある場合は`filter()`の後に続けて書く

### 辞書型として取得したい

```python
products = Product.objects.values()
```

`filter()`がある場合は`filter()`の後に続けて書く

### 特定のカラムの値のリストとして取得したい

```python
sizes = Product.objects.values_list("size", flat=True)
# flat=Trueがないとタプルのリストになる
```

`filter()`がある場合は`filter()`の後に続けて書く

### 結合(JOIN)して取得したい

`select_related(<関連オブジェクト名>)`を使用する

```python
orders = Order.objects.select_related("order_detail").all()
```

### 悲観ロックをしたい
`select_for_update()`を使用する

```python
products = Products.objects.select_for_update().filter(name__startswith="hoge")

with transaction.atomic():
    # 通常はトランザクション内部でしかロックは意味を持たない
    for product in products:
       # productの更新処理など
```

ノンブロッキング処理を行いたい場合は`nowait=True`を引数に追加する

### レコードの件数を数えたい

```python
product_count = Product.objects.count()
```

`filter()`がある場合は`filter()`の後に続けて書く

### 合計を求めたい

```python
from django.db.models import Sum

obj = Product.objects.aggregate(total_price=Sum("price"))
# -> obj = {"total_price": 24000}
```

`filter()`がある場合は`filter()`の後に続けて書く

### 最新（最古）のレコードを取得したい

```python
# 最新
latest_order = Order.objects.latest("created_on")
# 最古
earliest_order = Order.objects.earliest("created_on")
```

`filter()`がある場合は`filter()`の後に続けて書く

### 最初（最後）のレコードを取得したい

```python
# 最初
first_order = Order.objects.order_by("id").first()
# 最後
last_order = Order.objects.order_by("id").last()
```

`filter()`がある場合は`filter()`の後に続けて書く

### レコードの存在チェックをしたい

`if`の条件に直接書く場合、

```python
if Product.objects.filter(name="hoge").exists():
    # name="hoge"のレコードがある場合、ここに入る
```

変数に代入する場合、

```python
e = Product.objects.filter(name="hoge").exists()
# eにはTrueかFalseが入る
```

## 更新系
### レコードを挿入したい

```python
Product.objects.create(name="hoge", price=1200, size="M")
```

または、

```python
product = Product(name="hoge", price=1200, size="M")
product.save()
```

### レコードがあれば取得、なければ挿入したい

```python
product, created = Product.objects.get_or_create(
  name="hoge", 
  price=1200
  defaults={size="S"}
)
```
- name="hoge"かつprice=1200のレコードがあれば取得して変数`product`に代入する。変数`created`はFalseになる。
- name="hoge"かつprice=1200のレコードがなければname="hoge", price=1200, size="S"のレコードを作成し変数`product`に代入する。変数`created`はTrueになる。

### レコードがあれば更新、なければ挿入したい

```python
product, created = Product.objects.update_or_create(
  name="hoge", 
  price=1200
  defaults={size="S"}
)
```
- name="hoge"かつprice=1200のレコードがあれば、size="S"に更新して変数`product`に代入する。変数`created`はFalseになる。
- name="hoge"かつprice=1200のレコードがなければname="hoge", price=1200, size="S"のレコードを作成し変数`product`に代入する。変数`created`はTrueになる。

### 特定のレコードの特定のカラムを更新したい

```python
product = Product.objects.get(pk=1) # 条件を設定してから
product.name = "new onion"          # 書き換えて
# ...書き換えるカラムは複数でも良い...
product.save()                      # 更新
```

### 条件にマッチするレコードを一括更新したい
```python
products = Product.objects.filter(size="M") # 条件を設定してから
products.update(name="new onion")            # 一括更新
```

### 条件にマッチするレコードを一括削除したい
```python
products = Product.objects.filter(size="M") # 条件を設定してから
products.delete()                           # 一括削除
```