Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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

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

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

name price
apple 100
orange 100
banana 150

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

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

 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はモデルマネージャであり、モデルクラスと一緒に使う

 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行目の

 3 items = Item.filter(price=100) # エラー

では、モデルクラス.モデルマネージャのメソッド
と記述しており、モデルマネージャであるobjectsが抜けているので、エラーになります。

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

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

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

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

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

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

 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行目の

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

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

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

4行目の

 4 items = Item.objects.filter(price=100) # 正常(解説済)

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

続いて、6行目の

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

についてですが、

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

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

4行目と6行目の

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

については、

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

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

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

 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行目の

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

は、

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

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

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

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

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

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

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

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

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

 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)の記述が必要

from app.models import Item
items = Item.objects.filter(price=100) 
  • クエリセットインスタンスに対してモデルマネージャのメソッドを使う場合は、モデルマネージャ(objects)の記述は不要。記述するとエラーになる。
from app.models import Item

# filter()はクエリセットを返す 
items = Item.objects.filter(price=100)
items = items.order_by('name')
  • モデルマネージャのメソッドは、クエリセットインスタンスに対して連結して記述できる
from app.models import Item

# filter()はクエリセットを返すので、order_by()を連結できる 
items = Item.objects.filter(price=100).order_by('name')
  • モデルマネージャのメソッドは、クエリセットを返すもの・返さないものがある
from app.models import Item

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

# get()はクエリセットではなくモデルインスタンスを返す 
item = Item.objects.get(name='apple')
  • モデルインスタンスに対してはモデルマネージャのメソッドは使えない。なので、クエリセットを返さないメソッドには、モデルマネージャのメソッドを連結できない。
from app.models import Item

# get()はモデルインスタンスを返すのでexists()はエラーになる
item_exists = Item.objects.get(name='apple').exists()
  • モデルインスタンスに対して使うのは、モデルのインスタンスメソッドであり、モデルマネージャのメソッドではない。よって、モデルマネージャ(objects)の記述は不要。記述するとエラーになる。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした