【初心者向け】Djangoのモデル操作でobjectsが必要な場合・不要な場合を理解する

# はじめに

Djangoではじめてモデルを使った処理を書こうとした時に、モデルを操作するメソッドの手前に`objects`の記述が必要な場合・不要な場合が理解できず、エラーを頻発させて苦労しました。

この記事は、そんなDjango初心者の方を対象としています。

具体的にどのようなことを説明するかというと、以下のようなレコードを持ったItemモデルがあった場合に、

|name       |price|
|:----------|----:|
|apple      |100  |
|orange     |100  |
|banana     |150  |

下記の処理がそれぞれエラーになる・ならない理由を順に解説していきます。

注：便宜的に行番号を付けています

```python:
 1 from app.models import Item
 2
 3 items = Item.filter(price=100)         # エラー
 4 items = Item.objects.filter(price=100) # 正常
 5 items = items.objects.order_by('name') # エラー
 6 items = items.order_by('name')         # 正常
 7
 8 item = Item.objects.get(name='apple')  # 正常
 9 item = item.get(name='apple')          # エラー
10
11 new_item = Item(name='mango', price=500)
12 new_item.objects.save()                # エラー
13 new_item.save()                        # 正常
```
# 環境

- Django 2.1.4

# objectsはモデルマネージャであり、モデルクラスと一緒に使う

```python:
 1 from app.models import Item
 2
 3 items = Item.filter(price=100)         # エラー
 4 items = Item.objects.filter(price=100) # 正常
```

4行目に出てくる`objects`はモデルマネージャと呼ばれるものです（各モデルクラスは、このモデルマネージャを持っています）。

また、filter()メソッドは、このモデルマネージャのメソッドのひとつです。

なので、**モデルに対して**filter()メソッドのようなモデルマネージャのメソッドを使うときは
`モデルクラス.モデルマネージャ.モデルマネージャのメソッド`
と記述します。

（モデルマネージャのメソッドは、上記以外にクエリセットのインスタンスに対しても使えますが、それは後ほど説明します）

3行目の

```python:
 3 items = Item.filter(price=100) # エラー
```
では、`モデルクラス.モデルマネージャのメソッド`
と記述しており、モデルマネージャである`objects`が抜けているので、エラーになります。

モデルクラスの名前は先頭が大文字でしょうから、モデルのクラスやモデルのインスタンスの区別の理解が曖昧なうちは、

- 先頭が大文字のモデルオブジェクトに対しては`.objects`をつける
例）**B**log.objects.filter(author='John')
- それ以外の場合は、`.objects`は不要

と覚えても良いかもしれません。

なお、なぜモデルマネージャの名前が`manager`などではなく`objects`であるかというと、Djangoのデフォルトでそのように決まっているからです（ここでは触れませんが、別の名前に変更することもできます）。

# クエリセットには、モデルマネージャのメソッドを連結できる

クエリセットがどういうものであるかについては@okoppe8さんの以下の記事が詳しいのでそちらをご覧ください。

- [Django データベース操作 についてのまとめ](https://qiita.com/okoppe8/items/66a8747cf179a538355b)

```python:
 1 from app.models import Item
 2
 3 items = Item.filter(price=100)         # エラー（解説済）
 4 items = Item.objects.filter(price=100) # 正常（解説済）
 5 items = items.objects.order_by('name') # エラー
 6 items = items.order_by('name')         # 正常
```

まず、前提としてモデルマネージャのメソッドには、

- クエリセットを返すメソッド
- クエリセット以外（モデルのインスタンスなど）を返すメソッド

があります。

filter()メソッドは前者に該当し、クエリセットを返します。
なので、4行目でitemsにはクエリセットが代入されています。

また、5行目の

```python:
 5 items = items.objects.order_by('name') # エラー
```

に出てくるorder_by()メソッドは、filter()メソッドと同様にモデルマネージャのメソッドです。

よって、５行目は、
`クエリセット（のインスタンス）.モデルマネージャ.モデルマネージャのメソッド`
と記述していることになり、

4行目の

```python:
 4 items = Item.objects.filter(price=100) # 正常（解説済）
```

のような
`モデルクラス.モデルマネージャ.モデルマネージャのメソッド`
といった記述では無いため、5行目はエラーになります。

続いて、6行目の

```python:
 6 items = items.filter(price=100) # 正常
```

についてですが、

`クエリセット（のインスタンス）.モデルマネージャのメソッド`

となっていますが、これはエラーにはなりません。
クエリセットに対しては、さらにモデルマネージャのメソッドを連結できるためです。

4行目と6行目の

```python:
 4 items = Item.objects.filter(price=100) # 正常（解説済）
 6 items = items.order_by('name')         # 正常
```

については、

```python:
items = Item.objects.filter(price=100).order_by('name')
```

を2行に分けて実行していることになるので、6行目はエラーになりません。

# モデルのインスタンスに対しては、モデルマネージャのメソッドは使用できない

```python:
 1 from app.models import Item
 （略）
 8 item = Item.objects.get(name='apple') # 正常
 9 item = item.get(name='apple')         # エラー
```

8行目のget()メソッドも、モデルマネージャのメソッドです。

get()メソッドは、モデルのオブジェクトを1件だけ取得するメソッドであり、モデルマネージャのメソッドとしては

- クエリセットを返すメソッド
- クエリセット以外（モデルのインスタンスなど）を返すメソッド

の後者に該当し、返ってくるのはモデルのインスタンスです。
filter()メソッドやorder_by()メソッドのように、クエリセットは返しません。

なので、9行目の

```python:
 9 item = item.get(name='apple') # エラー
```

は、

`クエリセット（のインスタンス）.モデルマネージャのメソッド`
という記述ではなく、
`モデルのインスタンス.モデルマネージャのメソッド`
といった記述になっているのでエラーになります。

では、`モデルのインスタンス.モデルマネージャ.モデルマネージャのメソッド`という記述であればエラーにならないか、というとそうではなく、例えば、

```python:
# get()はモデルインスタンスを返す
item_obj = Item.objects.get(name='apple')　
item_obj = item_obj.objects.get(name='apple') # エラー
```

の2行目はエラーになります。

**モデルマネージャのメソッドは、モデルインスタンスで使うことはできないためです。**

また、
「**クエリセットに対しては**、さらにモデルマネージャのメソッドを連結できる」
と前述した通り、

```python:
item_exists = Item.objects.get(name='apple').exits() # エラー
```

は、エラーになります。
get()メソッドはクエリセットではなく、モデルのインスタンスを返しており、それに対してモデルマネージャのメソッドを連結することはできないためです。

# モデルインスタンスに対しては、モデルインスタンスのメソッドを使う

```python:
 1 from app.models import Item
（略）
11 new_item = Item(name='mango', price=500)
12 new_item.objects.save() # エラー
13 new_item.save()         # 正常
```

11行目では、モデルクラスItemから、モデルインスタンスnew_itemを生成しています。

モデルマネージャ(objects)のメソッドは、モデルインスタンスで使用することはできません。

なので、12行目のように

`モデルインスタンス.モデルマネージャ.・・・`

と記述することはありません。

そもそも、save()メソッドは、モデルのインスタンスメソッドですので、13行目のように

`モデルインスタンス.モデルインスタンスのメソッド`

と記述します。

# まとめ

- objectsはモデルマネージャと呼ばれるものである

- モデルマネージャのメソッドは、モデルクラスやクエリセットインスタンスに対して使用できる。モデルインスタンスに対しては使用できない。

- モデルクラスに対してモデルマネージャのメソッドを使う場合は、モデルマネージャ(objects)の記述が必要

```python:
from app.models import Item
items = Item.objects.filter(price=100) 
```

- クエリセットインスタンスに対してモデルマネージャのメソッドを使う場合は、モデルマネージャ(objects)の記述は不要。記述するとエラーになる。

```python:
from app.models import Item

# filter()はクエリセットを返す 
items = Item.objects.filter(price=100)
items = items.order_by('name')
```

- モデルマネージャのメソッドは、クエリセットインスタンスに対して連結して記述できる

```python:
from app.models import Item

# filter()はクエリセットを返すので、order_by()を連結できる 
items = Item.objects.filter(price=100).order_by('name')
```

- モデルマネージャのメソッドは、クエリセットを返すもの・返さないものがある

```python:
from app.models import Item

# filter()はクエリセットを返す 
items = Item.objects.filter(price=100)

# get()はクエリセットではなくモデルインスタンスを返す 
item = Item.objects.get(name='apple')
```

- モデルインスタンスに対してはモデルマネージャのメソッドは使えない。なので、クエリセットを返さないメソッドには、モデルマネージャのメソッドを連結できない。

```python:
from app.models import Item

# get()はモデルインスタンスを返すのでexists()はエラーになる
item_exists = Item.objects.get(name='apple').exists()
```

- モデルインスタンスに対して使うのは、モデルのインスタンスメソッドであり、モデルマネージャのメソッドではない。よって、モデルマネージャ(objects)の記述は不要。記述するとエラーになる。

# 参考

- [Django データベース操作 についてのまとめ](https://qiita.com/okoppe8/items/66a8747cf179a538355b)
- [現場で使える Django の教科書《基礎編》](https://www.amazon.co.jp/dp/B07GK7BWB7/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)
- [Django公式 クエリを作成する] (https://docs.djangoproject.com/ja/2.0/topics/db/queries/)
- [Django公式 QuerySet API reference]
(https://docs.djangoproject.com/ja/2.0/ref/models/querysets/#methods-that-do-not-return-querysets)
- [Django公式 マネージャの名前]
(https://docs.djangoproject.com/ja/2.0/topics/db/managers/#manager-names)