【Django】関数ベースビューとクラスベースビューでそれぞれTodoリストをつくり、2つの間の違いを見てみた(後編)
はじめに
長いので前編・後編に分割した。
- 前編:Django初期設定・関数ベースビュー
- 後編:クラスベースビュー
前編はこちら
クラスベースビューの前に
ここまで関数ベースビューでTodoリストをつくった。ここからは関数ベースビューをクラスベースビューに置き換えていく。
その前にCRUD操作を行う関数ベースビューのpath()
をコメントアウトしておく(name
がかぶるので)。クラスベースビューには別のname
をつけることもできるが、その場合テンプレート内のURL
を書きかえないといけないので面倒くさい。
urlpatterns = [
path("hello/", views.hello),
path("hello_str/<str:str>/", views.hello_str),
path("hello_slug/<slug:slug>/", views.hello_slug),
path("template/", views.template),
path("template_str/<str:str>/", views.template_str),
# path("list/", views.todo_list_view, name="list"),
# path("detail/<int:pk>/", views.todo_detail_view, name="detail"),
# path("create/", views.todo_create_view, name="create"),
# path("update/<int:pk>/", views.todo_update_view, name="update"),
# path("delete/<int:pk>/", views.todo_delete_view, name="delete"),
]
クラスベース|一覧
ビュー
比較用に関数ベースビューを再掲する。
def todo_list_view(request):
object_list = Todo.objects.all()
context = {"object_list": object_list}
return render(request, "todo/todo_list.html", context)
views.py
に以下のクラスベースビューを追加する。これで関数ベースビューのときと同じものを表示できる。model
の指定だけで一覧を表示でき、関数ベースビューと比べて格段に短い。データをすべて取得し、コンテキストに格納し、テンプレートに渡すという処理が全てショートカットされている。
-
model
は表示対象のモデル。ListView
にてmodel = Todo
と指定することは、queryset = Todo.objects.all()
と同じ。
from django.views.generic import ListView
from todo.models import Todo
⋮
class TodoListView(ListView):
model = Todo
以上が最低限の設定であるが、変数を追加することでカスタマイズできる。一部を紹介する。
-
queryset
単にmodel
を指定した場合は、model
のテーブルの全レコードを取得する。queryset
を使うと、特定の条件を満たすレコードのみを指定できる。 -
template_name
お好みのテンプレート名を指定する。指定しない場合はアプリ名/モデル名_list.html
ファイルを表示する(今回だとtodo/todo_list.html
)。 -
context_object_name
テンプレートに渡すオブジェクト名を指定する(デフォルトはobject_list
)。 -
ordering
指定したフィールドでレコードを並び替える。フィールドの前にハイフンをつけると降順、何もつけないと昇順になる。 -
paginage_by
1ページに表示されるレコード数。
class TodoListView(ListView):
queryset = Todo.objects.filter(text__contains="掃除") # textに「掃除」を含むレコード
template_name = "todo/hoge.html" # テンプレート名は"todo/hoge.html"
context_object_name = "todo" # テンプレートに渡すオブジェクト名は"todo"
ordering = "-updated_at" # 更新日時の降順
paginate_by = 10 # 1ページに10件表示
URL
urls.py
に以下を追加する。クラスベースビューでは、クラス名に.as_view()
を付けなければならない(以下同じ)。
urlpatterns = [
⋮
path("", views.TodoListView.as_view(), name="list"),
]
クラスベース|詳細
ビュー
def todo_detail_view(request, pk):
object = Todo.objects.get(pk=pk)
context = {"object": object}
return render(request, "todo/todo_detail.html", context)
views.py
に以下を追加する。一覧画面と同様にmodel
を指定するだけで表示できる。関数ベースビューでは表示するデータをTodo.objects.get(pk=pk)
で取得するが、その処理も必要ない。おそらくurls.py
の<int:pk>
から自動で選択しているのだと思う(たぶん)。
from django.views.generic import DetailView
from todo.models import Todo
⋮
class TodoDetailView(DetailView):
model = Todo
一覧画面と同様に、モデル以外にもいろいろ設定できる(一覧画面で説明したものは省略)。
-
template_name
指定しない場合は、アプリ名/モデル名_detail.html
ファイルを表示する。 -
pk_url_kwarg
プライマリーキーの名前をpk
から変更する。例えばpk_url_kwarg = "todo_id"
にすると、urls.py
のURLパターンは"detail/<int:todo_id>/"
となる。
class TodoDetailView(DetailView):
queryset = Todo.objects.filter(text__contains="あああ")
template_name = "todo/hoge.html"
context_object_name = "todo"
pk_url_kwarg = "todo_id" プライマリーキーの名前は"todo_id"
URL
urls.py
に以下を追加する。関数ベースビューと同様に、URLパターンに<int:pk>
を含める。
urlpatterns = [
⋮
path("detail/<int:pk>/", views.TodoDetailView.as_view(), name="detail"),
]
クラスベース|新規作成
ビュー
def todo_create_view(request):
if request.method == "POST":
text = request.POST["text"]
Todo.objects.create(text=text)
return redirect("todo:list")
if request.method == "GET":
return render(request, "todo/todo_form.html")
views.py
に以下のクラスベースビューを追加する。
-
model
- 表示対象のモデル
-
fields
- どのフィールドを扱うか指定
-
success_url
- フォームが正常に保存されたときに遷移するURL。今回は
todo:list
を指定しているので一覧画面にリダイレクトする。
- フォームが正常に保存されたときに遷移するURL。今回は
また
- 関数ベースビューでは
method
がPOST
かGET
で分岐していたが、クラスベースビューでは切り替える必要がない。 - テンプレート内に
{{ form }}
と書くと、自動で入力フォームが生成される(今回は関数ベースビューのときのテンプレートを使いまわしたいので<input type="submit" value="新規作成" />
のままにしている)。
from django.urls import reverse_lazy
from django.views.generic import CreateView
from todo.models import Todo
⋮
class TodoCreateView(CreateView):
model = Todo
fields = ("text",)
success_url = reverse_lazy("todo:list")
他にもいろいろ指定できる。
-
template_name
を指定しない場合は、新規作成画面としてアプリ名/モデル名_form.html
ファイルを表示する。
class TodoDetailView(DetailView):
model = Todo
fields = ("text",)
success_url = reverse_lazy("todo:list")
template_name = "todo/hoge.html"
context_object_name = "todo"
URL
urls.py
に以下を追加する。
urlpatterns = [
⋮
path("create/", views.TodoCreateView.as_view(), name="create"),
]
クラスベース|更新
ビュー
def todo_update_view(request, pk):
object = Todo.objects.get(pk=pk)
if request.method == "POST":
object.text = request.POST["text"]
object.save()
return redirect("todo:list")
else:
context = {"object": object}
return render(request, "todo/todo_update.html", context)
views.py
に以下を追加する。新規作成画面とほぼ同じである。
-
method
がPOST
かGET
か分岐する必要がない。 - どのデータを更新するか
Todo.objects.get(pk=pk)
で指定する必要もない。 -
template_name
を指定しない場合は、更新画面としてアプリ名/モデル名_form.html
ファイルを表示する(CreateView
と同じ名前)。新規作成画面と同じくtodo/todo_form.html
にしたら、新規作成画面と同じ画面になってしまうので、更新画面はtodo/todo_update.html
にした。 - テンプレート内に
{{ form }}
と書くと、自動で入力フォームが生成される。
from django.urls import reverse_lazy
from django.views.generic import UpdateView
from todo.models import Todo
⋮
class TodoUpdateView(UpdateView):
model = Todo
fields = ("text",)
template_name = "todo/todo_update.html"
success_url = reverse_lazy("todo:list")
URL
urls.py
に以下を追加する。
urlpatterns = [
⋮
path("update/<int:pk>/", views.TodoUpdateView.as_view(), name="update"),
]
クラスベース|削除
ビュー
def todo_delete_view(request, pk):
object = Todo.objects.get(pk=pk)
if request.method == "POST":
object.delete()
return redirect("todo:list")
else:
context = {"object": object}
return render(request, "todo/todo_confirm_delete.html", context)
views.py
に以下を追加する。
-
method
がPOST
かGET
か分岐する必要がない。 - どのデータを削除するか
Todo.objects.get(pk=pk)
で指定する必要もない。 -
template_name
がない状態でGET
した場合は、削除画面としてアプリ名/モデル名_confirm_delete.html
ファイルを指定する。
from django.urls import reverse_lazy
from django.views.generic import DeleteView
from todo.models import Todo
⋮
class TodoDeleteView(DeleteView):
model = Todo
success_url = reverse_lazy("todo:list")
URL
urls.py
に以下を追加する。
urlpatterns = [
⋮
path("delete/<int:pk>/", views.TodoDeleteView.as_view(), name="delete"),
]
GitHub
TODO:コードをGitHubにアップロードする
感想
この手の記事はネットにあふれている。こんな初歩的なことを、こんなに長々と記事にする必要があるのかと正直何回も思った。とはいえ説明を読んだからといって理解したことにはならない。自分でコードを書いて2割ほど、自分で説明してようやく5割ほど理解できるのではないかと思う。今後も記事を書くことにより理解を深めたい。
また今回は関数ベースビューとクラスベースビューの違いに焦点を当てるため、最低限の機能だけ実装した。次は機能を増やして、より多くの技術に触れたい。