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

この記事は約19分で読めます。
広告

Django公式チュートリアル、その2~4の内容を補足説明します。
開発環境はPython、Djangoのインストールが全く不要な
PaizaCloudを使ってます。
チュートリアルの完成品がどんな感じか分かった方がいいと思いますので
Herokuへデプロイしました。完成品Railwayデプロイ(毎月21日以降は停止してます)
完成品Renderにデプロイ(表示するまで数分かかることがあります)
今回ははじめての 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の書き方を決まったパターンとして考えるといいと思います。
【django】モデルのフィールドについて:フィールドの型・オプション一覧が分かりやすいです。
下のclassで定義したQuestionとChoiceはモデルと呼んでいます。
下のmodels.pyのイメージを図示すると(データは「API で遊んでみる」で入力されたデータです。)

Questionモデル

id自動割り振りquestion_textpub_date
1‘What’s up?’日付

Choiceモデル

id自動割り振りquestion(親)choice_textvotes
1‘What’s up?’‘Not much’0
2‘What’s up?’‘The sky’0
3‘What’s up?’‘Just hacking again’0

polls/models.py ファイル

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):#pub_dateの値が新しかったらTrueをreturn
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
#親となるモデルQuestionを指定してる。on_delete=は親のデータが削除されたときの動作を設定。models.CASCADEを設定すると、親側Questionのデータを削除すると、子側Choiceのデータも削除される。
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text
def __str__(self):  return self.○○の役割は下で解説してます。

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

サーバーが動いている場合はCTRL+Cでとめます。
ターミナルで
python manage.py makemigrationsを実行ターミナルで
python manage.py migrateを実行

目次へ

 Python シェル( データベース API )でデータを入れる

 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で大丈夫です。
パスワードは入力中、何も表示されません。また半角で入力してください。失敗したら多分CTRL+Cで抜けられると思います。だめだったらターミナルを閉じて、もう一度ターミナルを立ち上げcd mysiteしてpython manage.py createsuperuser
passwordなど簡単なパスワードを入力した場合Bypass password validation and create user anyway? [y/N]:と出ますがyでEnter。

python manage.py runserver

でサーバーを立ち上げると404エラーになるので
アドレスの最後にadminを追加すれば管理画面に行けます。
例https://localhost-○○.paiza-user-free.cloud:8000/admin
polls/admin.pyを以下のように変更するとChoiceも見れるようになります。

from django.contrib import admin

from .models import Question,Choice

admin.site.register(Question)
admin.site.register(Choice)

はじめての 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))
略

return HttpResponse(template.render(context, request))は
polls/templates/polls/index.htmltemplateとしてベースにします。
そこにデータベースから取り出したlatest_question_listcontextに入れて渡されます。
ベースとしてるindex.html内にあるテンプレートタグ{%・・・%}の処理でcontextのデータを取り出してindex.htmlを書き換えています。

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の汎用ビュークラス(ListViewとDetailView)を継承して
modelとtemplate_nameを書いておけばいいというはなしです。

latest_question_listとdef get_queryset(self):について補足すると
継承元のクラスListViewの機能が使われています。
def get_queryset(self):でリストにするqueryset(データ)を抽出してます。
context_object_nameはIndexViewをよんでるhtmlで扱うリストの名前を決めてます。
参考 https://yu-nix.com/archives/django-list-view/
Djangoのobject_listの名前を変更する方法

#汎用ビューを使う

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'

簡単な投票 (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へデプロイしました。 完成品Railwayデプロイ(毎月21日以降は停止してます)
完成品Renderにデプロイ(表示するまで数分かかることがあります)
データベース以外は
使い勝手は悪いですが、「その4」のプログラムそのままです。
ただしデータはPythonシェルで書き換えてます。
Herokuへのデプロイ方法は以下参照してください。(2022/11/末Herokuは有料になります)

目次へ

コメント

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