LoginSignup
2
3

More than 5 years have passed since last update.

Djangoのお勉強(3)

Last updated at Posted at 2018-02-19

はじめての Django アプリ作成、その 3の内容にそって試したことや調べた事を記載する。
前回の記事は Djangoのお勉強(2)

「とはいえ、こんな阿呆なことはやめましょう。」と記載されたところまでは、Djangoのお勉強(1) で試したのですっとばす。

逆に、ここから先は、DBにデータがないとつまらないと言うことがわかったので、データを入れる。

admin

DBにデータをセットするには、Db Browser for SQliteでデータを直接DBに書き込んだり、DBにデータをセットするviewを書いたり(普通はこっち)だけど、スーパーユーザーになってadmin画面からデータをセットする方法がある

スーパーユーザーを作成する

下記のコマンドでスーパーユーザーを作成します

python manage.py createsuperuser

Username (leave blank to use 'akira'): admin
Email address: test@hogehoge.jp
Password: ********
Password (again): ********
Superuser created successfully.

あまり安直なパスワードだとハネられます。

次にadmin画面でpollsアプリのmodelを触れるようにpolls/admin.pyを下記のように変更する

polls/admin.py
from django.contrib import admin

from .models import Question, Choice

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

この変更を行ってから

python manage.py runserver

コマンドでサーバーを起動し、作成したスーパーユーザーのidとパスワードでログインすると下記の画面が出て来る
Django003.jpg

POLLSの下にChoisesとQuestionsのふたつが確認出来る。
これらは、models.pyに作成されたクラスに対応している。

追加ボタンを押してデータを追加する。
見た目、models.pyに記載したフィールドそのままなので、悩まなくてもデータを追加出来る

viewを変更してデータを見てみる

とりあえず、チュートリアルのコードを使って見る
とは言っても、今まで色々追加しているので、余分なコード付き。

polls/views.py
from django.http import HttpResponse
from django.urls import reverse

from .models import Question

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)

    # Leave the rest of the views (detail, results, vote) unchanged

def subView(request, number):
    urlName = reverse('suburl', args=[number])
    return HttpResponse("これはsubViewです。{0} url={1}".format(number, urlName))

def repathView(request, number):
    urlName = reverse('re_path_url', args=[number])
    return HttpResponse("repathView。{0} url={1}".format(number, urlName))

この変更を加えて http://127.0.0.1:8000/polls/ にアクセスすると、Questionの question_textが表示される。

この処理内容をちょっこし調べてみる

latest_question_list = Question.objects.order_by('-pub_date')[:5]

で、データベースを読み込んでいるということは想像出来るが、なぜQuestion.objects.order_byなのかというのは、オブジェクトを取得するに下記のように記載されている

モデルの Manager を用いることで QuerySet を取得します。各モデルは少なくとも一つの Manager を持ち、デフォルトでは objects という名前を持ちます。以下のようにモデルクラスから直接アクセスしてください。

QuerySetについてはQuerySet APIに記載されていてorder_byの説明もここに記載されている

、、という事で、Question.objects.order_byによって、DBからQuestionテーブルのデータが読み込まれる。

output = ', '.join([q.question_text for q in latest_question_list])

で、Questionテーブルのquestion_textフィールドをカンマで区切って羅列した文字列を作る

return HttpResponse(output)

で、この文字列を出力する。
viewは、HttpResponse()で文字列を返すと、ブラウザ側へ送信される。

せっかくなので、表にしてみる

def subView(request, number):を変更して表にしてみる(subView以外はそのまま)

polls/views.py
from pytz import timezone

def subView(request, number):
    outStr1 ="<html>"\
            "<head>"\
            "<title>Question DBの中味を表示する</title>"\
            "</head>"\
            "<body>"\
            "<table>"\

    outStr2 ="</table>"\
             "</body>"\
             "</html>"

    latest_question_list = Question.objects.order_by('-pub_date')
    outList = ""

    for q in latest_question_list:
        print(q.pub_date.astimezone(timezone('Asia/Tokyo')))
        outList += "<tr><td>" + q.pub_date.astimezone(timezone('Asia/Tokyo')).strftime( '%Y/%m/%d %H:%M:%S') +"</td><td>" + q.question_text + "</td></tr>"
    output = outStr1+ outList + outStr2
    return HttpResponse(output)

 テンプレートを使う

ゴリゴリ文字列にHTMLを書くのは美しくないので、テンプレートを使う。

<プロジェクト名>/settings.pyに下記の設定がある

/settings.py
INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
      
      
      

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

この TEMPLATES ないの'APP_DIRS': True, の設定のお陰で、Djangoは、INSTALLED_APPSに登録されたアプリケーションディレクトリ内のテンプレートを検索するようになる。。とチュートリアルに書いてある。

それで、pollsディレクトリ内にtemplates/pollsTempというディレクトリを作ってテンプレートファイルを置く。
なんで、templatesの下でないかという事は、チュートリアルのテンプレートの名前空間というところに書かれているが、要するに、Djangoはtemplatesディレクトリが、どのアプリの下なのかを区別しないで、拾ってくるので、アプリ間で名前が衝突した時に、意図しないテンプレートファイルが採用されることになるらしい。
逆に、templatesの下に作るディレクトリ名がアプリ名と同じである必要はない(同じの方がわかりやすいとは思うけど)と思うので、あえて名前を変えてやってみた。

チュートリアルとは異なるけど、今、ゴリゴリ書いたHTMLと同じ出力になるように、テンプレートファイルを作ってみる。
、、なわけで、polls/templates/pollsTemp/repathView.htmlというファイルを作る

テンプレートの文法はThe Django Template Languageに記載されている。

変数
変数はテンプレート内では {{ 変数 }}の形式で記述する。
ドキュメントには色々書いてあるけど、とりあえず、これだけ理解。

フィルター
変数を表示する際のフォーマットを指定出来る
{{ 変数 | フィルター}}の形式で記述する。
用意されているビルトインフィルタについては組み込みフィルタリファレンスに記載されている。

前述のstrftime表記に対応する日付表示は、{{ 変数 | date:"Y/m/d " }}{{変数| time:"H:i:s" }}のようになる。

タグ
制御構文などについてはタグで表す。タグの中にpythonの文法が記述出来るわけではなくて、このテンプレート専用の構文となり{% タグ %}の形式で記述する。
用意されているビルトインタグについては組み込みタグリファレンスに記載されている。

for文による繰り返しは下記のようになる

{% for 変数 in リスト変数 %}
 :
{% endfor %}

if文による分岐は、下記のようになる
{% if 条件式1 %}
  式1
{% elif 条件式2 %}
  式2
{% else %}
  式3
{% endif %}

これを元に、テンプレートを作成する

polls/templates/pollsTemp/repathView.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Question DBの中味を表示する</title>
</head>
<body>

{% if latest_question_list %}
<table>
    {% for q in latest_question_list %}
    <tr>
        <td>{{q.pub_date | date:"Y/m/d " }}{{q.pub_date | time:"H:i:s" }}</td>
        <td>{{ q.question_text }}</td>
    </tr>
    {% endfor %}
</table>
{% else %}
    <p>No polls are available.</p>
{% endif %}
</body>
</html>

View側は、下記のようになる(repathViewだけ変更)

polls/views.py
def repathView(request, number):
    latest_question_list = Question.objects.order_by('-pub_date')
    template = loader.get_template('pollsTemp/repathView.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

テンプレートの読み込みはloader.get_templateで行う。
先に説明したとおり、アプリの下に作成したtemplatesからテンプレートを探してくるので、ここで指定するのは、templatesディレクトリの下からのパス名になる。

テンプレートに渡す変数は、辞書型にして渡す。

あとは、HttpResponseの引数としてtemplate.render(変数が格納された辞書, request)をreturnする

いつものように

python manage.py runserver

でサーバーを起動し
http://127.0.0.1:8000/polls/test1
にアクセスする。

先に、作成したsubView()の結果と比較するために
http://127.0.0.1:8000/polls/tes1
にアクセスすると、結果が同じである事がわかる。

ショートカット: render()

最後にviewにおける、templateの読み出しと送出を一度に行うrender()関数を使用してviewを書き換える

polls/views.py
from django.shortcuts import render

def repathView(request, number):
    latest_question_list = Question.objects.order_by('-pub_date')
    context = {
        'latest_question_list': latest_question_list,
    }
    return render(request, 'pollsTemp/repathView.html', context)

ここまでの最終ソース

ここまでで変更したファイルの内容を示す

<プロジェクト名>/setting.py
"""
Django settings for demoDjango project.

Generated by 'django-admin startproject' using Django 2.0.2.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'w2)3xu0&^)c)oa9s@-y#hg8&e=sy#08@6n656yzdvg6xgnt6_#'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'demoDjango.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'demoDjango.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
<プロジェクト名>/urls.py
"""demoDjango URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]
<アプリ名>/admin.py
from django.contrib import admin

from .models import Question, Choice

admin.site.register(Question)
admin.site.register(Choice)
<アプリ名>/models.py
from django.db import models


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


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
<アプリ名>/urls.py
from django.urls import path,re_path

from . import views
import re

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:number>', views.subView, name='suburl'),
    re_path('test(?P<number>[0-9])', views.repathView, name='re_path_url'),
]
<アプリ名>/views.py
from django.http import HttpResponse
from pytz import timezone

from .models import Question
from django.shortcuts import render

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)

    # Leave the rest of the views (detail, results, vote) unchanged

def subView(request, number):
    outStr1 ="<html>"\
            "<head>"\
            "<title>Question DBの中味を表示する</title>"\
            "</head>"\
            "<body>"\
            "<table>"\

    outStr2 ="</table>"\
             "</body>"\
             "</html>"

    latest_question_list = Question.objects.order_by('-pub_date')
    outList = ""

    for q in latest_question_list:
        outList += "<tr><td>" + q.pub_date.astimezone(timezone('Asia/Tokyo')).strftime( '%Y/%m/%d %H:%M:%S') +"</td><td>" + q.question_text + "</td></tr>"
    output = outStr1+ outList + outStr2
    return HttpResponse(output)

def repathView(request, number):
    latest_question_list = Question.objects.order_by('-pub_date')
    context = {
        'latest_question_list': latest_question_list,
    }
    return render(request, 'pollsTemp/repathView.html', context)
<アプリ名>/template/pollstemp/views.py
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Question DBの中味を表示する</title>
</head>
<body>

{% if latest_question_list %}
<table>
    {% for q in latest_question_list %}
    <tr>
        <td>{{q.pub_date | date:"Y/m/d " }}{{q.pub_date | time:"H:i:s" }}</td>
        <td>{{ q.question_text }}</td>
    </tr>
    {% endfor %}
</table>
{% else %}
    <p>No polls are available.</p>
{% endif %}
</body>
</html>
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3