Django公式チュートリアル-簡単な投票 (poll) アプリケーションの完成品をデプロイしました

この記事は約17分で読めます。

Django公式チュートリアル、その2~4の内容を補足説明します。
開発環境はPython、Djangoのインストールが全く不要な
PaizaCloudを使ってます。
チュートリアルの完成品がどんな感じか分かった方がいいと思いますので
Herokuへデプロイしました。完成品
今回ははじめての Django アプリ作成のその2からやっていきます。
こちらの記事の続きになります。

クラス

ここから先はPythonのクラスについて基本を確認してから進んだほうがいいです。
簡単にクラスを学べるところを紹介しておきます。

侍エンジニア塾【Python入門】クラスの使い方を簡単解説で楽々マスター

15. クラス | 中学生でもわかるPython入門シリーズ(Youtube)

Python全般の書籍はこれがおすすめです。

みんなのPython 第4版 Kindle版

はじめての Django アプリ作成、その2

続きをやっていきます。
はじめての Django アプリ作成、その2
ここはデータベースの接続と作成の章になります。

データベースの設定

mysite/settings.pyをデータベースに合わせて変更するのですが
SQLiteに関してはDjangoの場合何もしなくても使えるようになっています。

データベースと関係ありませんが
アプリケーションをプロジェクトに含めるには、
構成クラスへの参照を settings.pyのINSTALLED_APPS 設定に追加する必要があります。 
私がDjangoの勉強した動画では’polls’だけでしたが
‘polls.apps.PollsConfig’になっています。
‘polls.apps.PollsConfig’の中身を見るとname=’polls’となっているだけです。
どっちでもよさそうですがチュートリアルに従ったほうが無難ですね。

mysite/settings.py
INSTALLED_APPS = [
    'polls.apps.PollsConfig',

データベース作成

Djangoデータモデル概念図

基本的流れは

  • モデルを作成、変更する (models.py の中にモデルを定義)
  • モデルの作成、変更をデータベースに反映させるマイグレーションファイルを作成するために python manage.py makemigrations を実行。
  • データベースにマイグレーションファイルを適用するために python manage.py migrate を実行します。

具体的には polls/models.pyがあるので以下のように全部書き換え
あとから青色部分と緑色部分を追加していますが最初に追加しても大丈夫です。

models.pyの中身を理解しようとして疑問がわいてきましたが
modelsの内部クラスmodels.Modelを継承しmodelsのメソッドを使用しているのはなぜ?
データはmodelsのインスタンスとして定義してるのか?

ここで立ち止まらず、こちらを見てmodels.pyの書き方を
決まったパターンとして考えることにしました。
【django】モデルのフィールドについて:フィールドの型・オプション一覧が分かりやすいです。
下のclassで定義したQuestionとChoiceはモデルと呼んでいる。

import datetime

from django.db import models
from django.utils import timezone

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

モデルが定義で来たのでデータベースへ反映させます。

ターミナルで
python manage.py makemigrationsを実行ターミナルで
python manage.py migrateを実行

目次へ

 Python シェルでデータを入れる

 Python シェル(対話的にプログラムを実行できる)で
データベースの中身を操作することができます。

python manage.py shell でPythonシェル起動

色々操作していますが意味が分からなくても
>>>の部分を入力してEnterしていってください。
データベースにデータが入ります。
エラーが出たらもう1回python manage.py makemigrationsからやり直してください。

各操作の内容、例えば
Question.objects.all()
の説明はこちらが分かりやすいです。

【Django】データベース操作(取得・作成・更新・削除):ORMの利用

途中コードを追加していますが、これについて補足すると。
__str__は特殊メソッドでオブジェクトを文字列型に変換するときに呼び出される関数
print()関数でオブジェクトを表示するときも暗黙に呼び出される

Questionの中身を見るメソッドを実行すると

>>> Question.objects.all() Questionの中身を見るメソッド
<QuerySet [<Question: Question object (1)>]>
データがあることはわかるが何なのかわからない

polls/models.pyのclass Question(models.Model):以下を追加したことで中身がわかるようになる
   def __str__(self):
        return self.question_text

>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
データの中身が What's up?であることがわかる。
choice_setについて

models.pyでchoice_setなんて定義してないのに出てきている。

例えばq.choice_set.create(choice_text='Not much', votes=0)
これは以下2点から推測できる。

・Choiceクラスは以下でQuestionクラスと紐づけされている
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)

・1対多の参照がオブジェクト.モデル名(小文字)_set.all()で可能らしい
参照元【Django】1対多の関係( related_name, _set.all() )について

使われてる例で説明すると
q = Question(question_text="What's new?", pub_date=timezone.now())
qはQuestionのオブジェクト
q.choice_set.create(choice_text='Not much', votes=0)
はオブジェクト.モデル名(小文字)_set.all()の形になっている。
.all()ではなく.create()ではあるが。

最小限以下だけやってデータを入れておけば先に進んでも問題はありません。

from polls.models import Choice, Question
from django.utils import timezone
q = Question(question_text="What's new?", pub_date=timezone.now())
q.save()
q.question_text = "What's up?"
q.save()
q.choice_set.create(choice_text='Not much', votes=0)
q.choice_set.create(choice_text='The sky', votes=0)
exit

Pythonシェルから出るにはexitです。
目次へ

Django Admin

 Python シェルはターミナル操作でデータを操作しました。
それに対してAdminはアプリを起動した状態でAdminに入り
データベースを操作できます。
指示通りにやればできます。
途中メールアドレスは入力しないで、そのままEnterで大丈夫です。
サーバーを立ち上げると404エラーになるので
アドレスの最後にadminを追加すれば管理画面に行けます。

はじめての Django アプリ作成、その 3

続いてはじめての Django アプリ作成、その 3
この章の目的はテンプレートを使ってHTML形式で出力する方法です。

もっとビューを書いてみる

/polls/番号/にアクセスしたら番号をviewsに渡し
“You’re looking at question 番号.と出力するプログラム
指示通りプログラムするとYou’re looking at question 34.と出てきます。

補足説明
polls/urls.py 
urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),

urlが/polls/番号/の時polls/views.pyが呼ばれquestion_idが渡される

polls/views.py 
def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

実際に動作するビューを書く
データベースにある最新の 5 件の質問項目をカンマで区切り、日付順に表示するビューです。
実行結果は、その2でWhat’s up?しかデータベースに入れてないので
それしか表示しません。
データを増やすために、その2でやったadminに入り。
Questionsをクリックすると以下の画面が出ます。

ADD Questionで下の画面が出るので
それぞれ入力とクリックしてsaveするとデータベースにデータが登録されます。
5個入れたらプログラムを動かしてみましょう。

補足説明
polls/views.py

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

Questionモデルの一覧をpub_dateの降順で5個取得
'-pub_date'の-は降順を表す。昇順の場合はなし。

question_textの中身が,区切りで降順に5個表示される。

Djangoで一般的なテンプレートを使う方式に変更
htmlを使って出力する。
といってもリスト表示になるだけです。
views.pyを指示通りに変更。
Pizacloudの左に表示してるpollsディレクトリを右クリックして新規ディレクトリ作成
でtemplatesを作り、その下にpollsディレクトリを作る。
そのpollsを右クリックして新規ファイル作成でindex.htmlを作成。
完成したディレクトリ構成は polls/templates/polls/index.html 

テンプレート作成
チュートリアル掲載コードはHTMLファイルの基本部分が抜けているので
完全な HTML ドキュメント を参照して
<p>This is my page</p>の部分を変更すると以下になる。

polls/templates/polls/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>My test page</title>
  </head>
  <body>
#views.indexからわたされたコンテキストlatest_question_listに値が入っているか
    {% if latest_question_list %}
#Questionモデルのリンクリスト作成(リンク先は3章の最初にpolls/urls.py で作られてる)
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
  </body>
</html>

テンプレートについてはこちらが分かりやすいです。
【django】テンプレートとは:使用方法(templatesフォルダ・変数・タグ )

polls/views.pyの補足説明

from django.http import HttpResponse
from django.template import loader
from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

polls/templates/polls/index.htmltemplateとしてロードし
データベースから取り出したlatest_question_listcontextとしてindex.htmlに渡します。
template.render(context, request)でHttpResponseで出力できるように変換
HttpResponseでユーザーに出力しています。

viewの役割はデータベースから引き出したデータ(context)とtemplateを使ってユーザーへ
返信する画面を作ることのようですね。

目次へ
ショートカット: render()
ここは上のpolls/views.pyを
別の書き方で書いているだけでやっていることは同じです。

404 エラーの送出ショートカット: get_object_or_404()
テンプレートシステムを使う
この3つはdetailのページを作ってます。

テンプレート内のハードコードされたURLを削除
ここはpolls.urlsで定義したnameの役割です。

polls.urls
path('<int:question_id>/', views.detail, name='detail'),が
path('specifics/<int:question_id>/', views.detail, name='detail'),に変わっても

polls/index.htmlの
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>を

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
に変えてしまえばpolls.urlsの変更だけで済むという話です。
要するにpolls.urlsのname='***'を使って
テンプレートでは{% url '***' ○○○%}の形でURLを表現したほうがいいということです。
またname='detail'としてるurls.pyではpolls/が含まれていないように見えますが
mysite/urls.pyでpolls/urls.pyが呼ばれているのでpolls/が含まれています。

URL 名の名前空間
同名のビューを違うアプリと区別するための方法が書かれています。
urls.pyでapp_name = ‘polls‘というようにアプリ名を明記し
テンプレでは{% url ‘polls:detail’に変更するということです。

はじめての Django アプリ作成、その 4

はじめての Django アプリ作成、その 4

polls/views.py の最終的な形はDjangoの汎用ビュークラスに
modelとtemplate_nameを書いておけばよさそうです。
しかしIndexViewではmodelではなくcontext_object_nameが使われています。

latest_question_listとdef get_queryset(self):関係について補足すると
get_queryset(self):の戻り値が context_object_name =
で指定したlatest_question_listに入るという仕組みのようです。

#テンプレートに渡すモデルを処理してから渡す場合
def get_queryset(self):で処理し戻り値を表す文字列をcontext_object_nameに指定する。

class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]

#modelとtemplateだけ指定すればいい。
class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'

参照元Djangoのobject_listの名前を変更する方法


簡単な投票 (poll) アプリケーションの最終的な形になります。
指示通りやれば完成します。

質問がWhat’s upだけだとよくわからないので質問を追加してみます。
ターミナルでPythonシェルを立ち上げ
既に登録してある質問を調べて、ちゃんとした質問に変更します。
選択肢も追加します。

python manage.py shell
from polls.models import Choice, Question

Question.objects.all()
 <QuerySet [<Question: What's up?>, <Question: test2>, <Question: last>]>
q=Question.objects.get(question_text="test2")
q.question_text='Pythonは好きですか?'
q.save()
q.choice_set.create(choice_text='好き', votes=0)
q.choice_set.create(choice_text='嫌い', votes=0)
q.choice_set.all()で追加されていることを確認
exit

完成品

データベースSQLiteをPostgreSQLへ変更し
Herokuheへデプロイしました。 完成品
データベース以外は
使い勝手は悪いですが、「その4」のプログラムそのままです。
ただしデータはPythonシェルで書き換えてます。
Herokuへのデプロイ方法は以下参照してください。

目次へ

コメント

タイトルとURLをコピーしました