はじめに
Django 初心者が簡単なアプリをつくる3の続き。前回はCRUDのC、つまりCreate部分をClass-based-viewで作成した。今回はClass-based-viewからFunction-view(関数ビュー)に書き換えて、2つを比較しながらどうやってDjangoは動いているのかを追いかけてみたい。
初心者が簡単なアプリをつくるシリーズ目次
-
Django 初心者が簡単なアプリをつくる1
- 準備・Djangoの全体像・models.pyの作成
-
Django 初心者が簡単なアプリをつくる2
- Read部分の実装(urls.py・views.py・templateファイルの作成)
-
Django 初心者が簡単なアプリをつくる3
- Create部分の実装(form.py等の作成)
- Django 初心者が簡単なアプリをつくる4(←今ココ)
- Class-based-view と Function-view の比較
- Django 初心者が簡単なアプリをつくる5(完結)
- Update部分・Delete部分の実装
環境
Ubuntu 20.04 LTS
Python 3.8.2
Django 3.02
前提
プロジェクト名はconfig、アプリ名はmyapp とする。つまり以下2つのコマンド実行済み
(myenv)$ django-admin startproject config .
(myenv)$ python manage.py startapp myapp
templatesディレクトリはmanage.pyと同じ階層に作成、setting.pyも修正済み。(「初心者が簡単なアプリをつくる1」を参照のこと)
1. Class IndexView を def index に書き換える
class IndexView(generic.ListView):
template_name = 'myapp/index.html'
context_object_name = 'movie_list'
queryset = Movie.objects.all()
# 以下が Function-view
def index(request):
movie_list = Movie.objects.all()
return render(request, 'myapp/index.html', {'movie_list': movie_list})
実は2行で終わった… Function-view の方がコードが短い。やっていることは
- model(データベース)の Movie からデータをすべて集めて movie_list というオブジェクト(インスタンス)にまとめる
- movie_listを材料にしてindex.htmlにレンダリングする
学んだ知識
1-1. requestって何?
あるページがリクエストされたとき、Django はリクエストに関するメタデータを含んだ HttpRequest オブジェクトを生成します。それから Django は HttpRequest をビュー関数の最初の関数として渡し、適切なビューを読み込みます。あらゆるビューは HttpResponse オブジェクトを返す必要があります。
引数のrequestって何だ?誰が何をリクエストしてんだ?と疑問だったが、**requestの正体はHttpRequestのオブジェクト(インスタンス)**だった。
1-2. render()って何?
Combines a given template with a given context dictionary and returns an HttpResponse object with that rendered text.
render関数は辞書型のデータをHTMLファイルに整形して表示してくれると。render()で最終的に出てくるのはHttpResponseのオブジェクト(インスタンス)。
1-3. objectsって何?
Each non-abstract Model class must have a Manager instance added to it. Django ensures that in your model class you have at least a default Manager specified. If you don’t add your own Manager, Django will add an attribute objects containing default Manager instance.
**model(データベース)に入っているデータを引っ張ってくるためにManagerというモノがあって、その名前がobjects。**だからMovie.objects.all()
というのは「Movieのobjects(Manager)よ、すべてのデータを持って来い!」っていう意味になる。
2. Class MovieDetailView を def moviedetail に書き換える
class MovieDetailView(generic.DetailView):
model = Movie
template_name = 'myapp/detail.html'
# ここからFunction-view
def moviedetail(request, pk):
m = Movie.objects.get(pk=pk)
return render(request, 'myapp/detail.html', {'movie': m})
ここも2行で済んだ。(request, pk)
部分のpk
はurls.pyに記述してあるpath('movie/<int:pk>/',
の<int:pk>
のこと。ここは変更可。もしもurlを<int:movie_id>
に変えるならば、ここを(request, movie_id)
に変える。この関数でやっていることは、
- Movie(データベース)に入っている、pk(プライマリーキー)がリクエストと一致しているデータをgetしてmというオブジェクトにする
- mをmovieとしてdetail.htmlにレンダリングする
ってことになるんだろう。
3. Class RegisterDirectorView を def registerdirector に書き換える
class RegisterDirectorView(generic.CreateView):
model = Director
form_class = DirectorForm
template_name = 'myapp/register.html'
def get_success_url(self):
return reverse('myapp:registermovie')
# ここからFunction-view
def registerdirector(request):
if request.method == "POST":
form = DirectorForm(request.POST)
if form.is_valid():
d = form.save(commit=False)
d.save()
return redirect('myapp:registermovie')
else:
form = DirectorForm()
return render(request, 'myapp/register.html', {'form': form})
Class-based-viewとFunction-viewの大きな違いが現れるのはここCreateViewのところ。仕組みを把握するためにはFunction-viewの方が理解しやすい。
学んだ知識
3-1. request.method == "POST" って何?
ブラウザはサーバとHTTPでやりとりしているけれども、そのやりとり(method)がPOSTということ。実はregister.htmlに<form method="POST">
と記述があり、これはsaveボタンが押されたらPOSTとしてデータを送信するという意味で、この行と絡んでくる。
3-2. form = DirectorForm(request.POST)って何?
request.POST は辞書のようなオブジェクトです。キーを指定すると、送信したデータにアクセスできます。この場合、 request.POST['choice'] は、選択された選択肢の ID を文字列として返します。 request.POST の値は常に文字列です。
つまりrequest.POST
に入力したデータが入っていると。それをDirectorFormにあてはめてformというオブジェクトを作っているということになる。
3-3. form.is_valid()って何?
The primary task of a Form object is to validate data. With a bound Form instance, call the is_valid() method to run validation and return a boolean designating whether the data was valid:
formというオブジェクトが有効であるかどうか。入力漏れ等あるとこれで弾いてくれる。バリデーション(入力したデータが間違ってないかチェックする)とかデータのクリーニング(データを使いやすい形に整形する)とかいう言葉を知らなかったので、はじめはちんぷんかんぷんだった。
3-4. d = form.save(commit=False)って何?
This save() method accepts an optional commit keyword argument, which accepts either True or False. If you call save() with commit=False, then it will return an object that hasn’t yet been saved to the database. In this case, it’s up to you to call save() on the resulting model instance. This is useful if you want to do custom processing on the object before saving it, or if you want to use one of the specialized model saving options. commit is True by default.
データを入れたformをsaveしてdというオブジェクトにするが、まだデータベースには入っていない状態。なんでわざわざこんなことをしてんだろうと思ったが、どうもこのformに後からデータを追加してあらためてデータベースに保存することがあるかららしい。一時保存みたいなものか。で、次行のd.save()
でようやくデータベースに保存されると。
その他
redirect()は「()内のアドレスに飛んでいけ」、else以下は「空のformを作ってregister.htmlにレンダリングしろ」と。こんな風に学んだ知識を活用したらどんどんコードが読めるようになる。なんか自分が進化したようで嬉しい。
4. Class RegisterMovieView を def registermovie に書き換える
class RegisterMovieView(generic.CreateView):
model = Movie
form_class = MovieForm
template_name = 'myapp/register.html'
def get_success_url(self):
return reverse('myapp:movie_detail', kwargs={'pk': self.object.pk })
# ここからFunction-view
def registermovie(request):
if request.method == "POST":
form = MovieForm(request.POST)
if form.is_valid():
m = form.save(commit=False)
m.save()
return redirect('myapp:movie_detail', pk=m.pk)
else:
form = MovieForm()
return render(request, 'myapp/register.html', {'form': form})
コードはほぼ前回と一緒。あえて言うならredirect('myapp:movie_detail', pk=m.pk)
部分だが、「mというオブジェクトのpkをmovie_detailのpkとして飛んでいけ」という意味になるだろうか。
5. Class WritingLogView を def writinglog に書き換える
class WritingLogView(generic.CreateView):
model = Log
form_class = LogForm
template_name = 'myapp/register.html'
def get_success_url(self):
return reverse('myapp:movie_detail', kwargs={'pk': self.object.movie.pk })
# ここからFunction-view
def writinglog(request):
if request.method == "POST":
form = LogForm(request.POST)
if form.is_valid():
l = form.save(commit=False)
l.save()
return redirect('myapp:movie_detail', pk=l.movie.pk)
else:
form = LogForm()
return render(request, 'myapp/register.html', {'form': form})
コードはほぼ一緒。「読める!読めるぞ!!」(by ムスカ)
あとがき
Class-based-Viewの方が最終的には楽に書けるんだろうけど、Function-viewの方が仕組みが把握しやすいし細かなところに手が届く感じ。オートマ車とマニュアル車みたいなものだろうか。そういやFunction-viewに書き換えたら、ちゃんとurls.pyも書き換えないと動かないので悪しからず。次回はCRUDのUDつまりUpdateとDeleteを実装予定。今回みたいに両方並べてコードを書いたほうがたぶんわかりやすいハズ。間違い等ありましたら、ご指摘ご指導よろしくお願いいたします。ゴールは近い…のか?