【解説付き】Django チュートリアル その2 -後編-

# はじめに

これは、[【解説付き】Django チュートリアル その2 -前編-](https://qiita.com/murabo/items/9fc69b419b4415d7dbeb)に続き、
[はじめての Django アプリ作成、その2](https://docs.djangoproject.com/ja/2.2/intro/tutorial02/)の後半部分に解説をつけて初学者にわかりやすく学んでいただきたいというためのものです。

その2は、ボリュームがあるため前編/後編とさせていただいております。

前編は[こちら](https://qiita.com/murabo/items/9fc69b419b4415d7dbeb)

# 対象
 - pythonはなんとなくわかってるけど、djangoを覚えたい
 - 業務でdjango触ってたけど、きちんと理解したい

上記の方々を対象としています。
環境は、Macを使用して進めていきますので、Windowsユーザーの方は、Windows環境に読み替えてついてきていただければと思います。

# 目次
 - [【解説付き】Django チュートリアル その１](https://qiita.com/murabo/items/b99fd8b137e348b1358c)
 - [【解説付き】Django チュートリアル その2 -前編-](https://qiita.com/murabo/items/9fc69b419b4415d7dbeb)
 - 【解説付き】Django チュートリアル その2 -後編-

# API で遊んでみる
`Python 対話シェルを起動して、 Django が提供する API で遊んでみましょう。`

と、チュートリアルでは突発的にこんな事を言ってきます。

**`Python対話シェル`**、**`API`** といきなり言われてもわからないですよね。



## API

APIとは、Application Programming Interfaceの略です。
「アプリケーションプログラムとの、コミュニケーションを行うもの」という解釈になるかと思います。

プログラムとお話する痛いヤツと思われそうですが、今回のケースでいうと、
「djangoの機能を、対話的に実行するための手段」
となります。

なんとなく、イメージが付きやすくなってきたと思います。

※ APIには、`WEB API`というものもあります(一般的にこちらを指すことが多い)
これについては、APIを作る解説として、改めて記事化しようと思います。

## Python対話シェル

Python対話シェルとは、pythonが提供するAPIです。




 Python シェルを起動するには、以下のコマンドを実行します


```
$ python manage.py shell
```

`python` コマンドから、 `manage.py` を実行することで djangoと接続できます。

djangoの対話シェルに入ったら データベースのAPIを触ってみましょう


![managepy.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/bfd73485-dd78-edaf-9118-2bb1b500e1e9.gif)

<details><summary> 上の内容をテキスト化したもの(クリックすると表示されます)</summary><div>



```console
# pollsのmodelsファイルから、Choice, Questionを読み込む
>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# Questionの全データを取得する(現時点では0件)
>>> Question.objects.all()
<QuerySet []>

# djangoのutileファイルから、timzeoneを読み込む
>>> from django.utils import timezone
# Questionクラスのインスタンスを作る。
# question_textに"What's new?"、pub_dateに、現在エリアの現在時刻を指定
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# インスタンスのsave()メソッドを実行し、DBに保存
>>> q.save()

# DBに保存されることで、IDが採番される
>>> q.id
1

# インスタンスのフィールドの値を確認
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# question_textを"What's up?"に変更
>>> q.question_text = "What's up?"
# .save()を行うことで、変更内容がDBに反映される
>>> q.save()

# データが入ったので、改めて、全件取得を実行してみる
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

```

</div>

取得したデータが、
`<Question: Question object (1)>`と表示されてますよね
この状態では、どんなデータかわからないため、
`polls/models.py`の`Question` モデルを編集しましょう。
 `__str__()` メソッドを `Question` と `Choice` に追加します。


```python:polls/models.py
from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text
```


`__str__()`を書くことで、対話シェルでの見栄えだけではなく、Djangoで自動生成されるadmin(管理画面)の
一覧データ表示時にも使用されます。
モデルには、`__str__()` メソッドをつけるようにしましょう。


デモ用にカスタムのメソッドを追加してみましょう


```python:polls/models.py
import datetime

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


class Question(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
```


`import datetime` と `from django.utils import timezone` を追加しました。
これは、 Python の 標準モジュール `datetime` と Django のタイムゾーン関連ユーティリティの `django.utils.timezone` を利用するために呼び出しています。 
Python でのタイムゾーンのついて詳しく知りたい方は、[タイムゾーンサポートドキュメント](https://docs.djangoproject.com/ja/2.2/topics/i18n/timezones/) を参照してください。


もう一度 `python manage.py shell` を実行して確認してみましょう。
(ソースを変更した場合、対話モードを開き直さないと、変更が反映されないので気をつけてください)

![managepy2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/2a9e9cf8-555a-02f2-cbdf-c371416e26cc.gif)

<details><summary> 上の内容をテキスト化したもの(クリックすると表示されます)</summary><div>

```console

>>> from polls.models import Choice, Question

# __str__()を追加したことで、オブジェクトがわかりやすくなりました。
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# データベースから条件でフィルターして(絞り込んで)取得することができます。
# .all() を filter(id=1) と変えることで、IDが1のものを取得してくれます。
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
# filter(question_text__startswith='What') と変えることで、Whatから始まるデータを取得してくれます。
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# Questionのpub_dateを今年のデータを指定します
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# .get(id=2)を指定すると、条件とマッチする１件を取得します。
# データがない場合や、複数件がhitするような指定の場合は、エラーとなります。
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>

# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>

# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>

# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
```

</div>

モデルのリレーションについては[リレーション先オブジェクトにアクセスする](https://docs.djangoproject.com/ja/2.2/ref/models/relations/) を参照してください。 
API を通じた、フィールドルックアップのためのダブルアンダースコアの使い方は [フィールドルックアップ](https://docs.djangoproject.com/ja/2.2/topics/db/queries/#field-lookups-intro) を参照してください。
データーベース API の詳細は [データベース API リファレンス](https://docs.djangoproject.com/ja/2.2/topics/db/queries/) を参照してください。



# Django Adminの紹介

**設計思想**

コンテンツの追加や変更、削除を行うための管理サイトを作ることはクリエイティブではなく、面倒に感じられることが多いと思います。
そのため、Djangoはモデル(データ)のための管理インタフェースを自動的に生成してくれます。

もともとDjangoは、ニュース系のサイトを管理する目的で開発されました。
ニュース系のサイトでは、 「コンテンツの作成者 (content publisher)」と「公開 (public) 」サイトを明確に区別しています。
サイトの管理者は、新たな話題やイベント、 スポーツのスコアなどの入力にシステムを使い、コンテンツは公開用サイト上で表示されます。 
Django は、サイト管理者向けのadmin(管理)機能を提供しています。

admin はサイトの訪問者でなく、サイト管理者に使われることを意図しています。

## 管理ユーザーを作成する

最初に私達はadminサイトにログインできるユーザーを作成する必要があります。

下記のコマンドを実行します。

```console
$ python manage.py createsuperuser
```

好きなユーザー名を入力しEnterを押してください。

```console
Username: admin
```

emailアドレスを入力します。

```console
Email address: admin@example.com
```

最後のステップはパスワードの入力です。
確認のために、パスワードの入力を2回求められます。

```console
Password: **********
Password (again): *********
Superuser created successfully.
```

## 開発サーバーの起動

Django adminサイトはデフォルトで有効化されます。
開発サーバーを起動して触れてみましょう。

もしサーバーが起動していなかったら、このようにして起動しましょう

```console
$ python manage.py runserver
```

次はブラウザで、`http://127.0.0.1:8000/admin/` にアクセスします。
以下のような admin のログイ ン画面が表示されるはずです

![admin01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/93daa783-08ec-f187-4979-c7caee0ab439.png)

translation がデフォルトで on になっているため、ログイン画面はあなたの言語で表示されるかもしれません。
これは、あなたのブラウザの設定と、 Django がその言語に翻訳されているかどうかによります。

## admin サイトに入る

前のステップで作成したスーパーユーザーのアカウントでログインしてみましょう。 
Django admin のインデックスページが表示されるはずです

![admin02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/ab990a0d-f980-708a-122b-cdbb557b23e6.png)

groups と usersなどの編集可能なコンテンツが表示されるはずです。
これらは Django に含まれる認証フレームワーク django.contrib.auth によって提供されます。


## Poll アプリを admin 上で編集できるようにする

polls アプリは管理画面に表示されていましたか？ 
admin のインデックスページを見ても表示されていませんね。

`Question` オブジェクトの`admin` インタフェースを`admin`に追加する必要があります。
追加するために、`polls/admin.py` に下記のように編集しましょう

```python:polls/admin.py
from django.contrib import admin

from .models import Question

admin.site.register(Question)
```

## admin の機能を探究してみる

私たちが `Question` を追加したので、 Django は admin インデックスページに表示してくれました。

![admin03t.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/1430f0d1-574e-5ed0-a9c9-3a463508a4f6.png)


`Questions` をクリックしましょう。
questions のための "change list" ページが表示されます。
このページにはデータベース中のすべての question が表示され、あなたはその中のひとつを選んで変更することができます。
ここには、上記で作成した "What's UP?" question もあります

![admin04t.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/83ee8d71-daf8-7c5c-adca-c22ff6a61f18.png)

"What's up?" questionをクリックして編集してみましょう

![admin05t.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/32041ad5-b590-ff25-b21b-812357077f41.png)

※下記日本語版
<img width="575" alt="スクリーンショット 2019-08-22 21.02.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/8ca4b81c-8bb1-ecc5-ef38-154d07974c9c.png">


以下の点に注意してください

- 入力フォームは `Question` モデルから自動的に生成されます。
- モデルのフィールドの型 (DateTimeField 、 CharField など) に応じて、HTML 入力フォームと対応しています。<br/>
- `DateTimeField` は JavaScript ショートカットがついています。<br/>日付 (dates) のカラムには「今日 (today)」 へのショートカットとカレンダーポップアップボタンがあります。 <br/>時刻 (times) には「現在 (now)」へのショートカットと、よく入力される時刻のリストを表示するポップアップボタンがあります。


ページの下部には下記のボタンが用意されています。

- **保存 (Save)** – 変更を保存して、このモデルの一覧のページに戻ります。
- **保存して編集を続ける (Save and continue editing)** – 変更を保存して、このオブジェクトの編集ページをリロードします。
- **保存してもう一つ追加 (Save and add another)** – 変更を保存して、新たに新規追加するための空の編集ページを表示します。
- **削除 (Delete)** – 削除確認ページを表示します。

もし「Date published」の値が、チュートリアルその1 で作成した questionと一致しないのであれば、`TIME_ZONE`の設定が漏れていた可能性があります。
設定を変更して、ページをリロードし、正しく表示されるか確認してください。

`今日`や`現在`ショートカットをクリックして、「Date published」を変更してみましょう。
変更したら、「保存して編集を続ける」を押します。

次に、右上に ある「履歴 (History)」をクリックしてみましょう。
ユーザが管理サイト上でオブジェクトに対して行った変更履歴の全てを、変更時刻と変更を行ったユーザ名付きでリストにしたページが表示されます

![admin06t.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38197/750798b2-b7fe-8131-6e80-c1feb7efeac2.png)

Modelの `API` や `admin` サイトに慣れてきたら、 チュートリアルその3 を読んで、 `polls` アプリにビューをさらに追加する方法を学習しましょう。


# 次回予告

ここまでがdjango チュートリアルのその2 -後編- でした。

既にdjangoに触れてる人や、多言語に詳しい人ならわかるけど、初めての人にはわかりにくい部分が多いと思います。
本家のチュートリアルに補足情報を盛り込んだ形で記載しているので理解の手助けになれれば幸いです。

次回は、[はじめての Django アプリ作成、その3](https://docs.djangoproject.com/ja/2.2/intro/tutorial03/)について書いていこうと思います。