昼飯データベースを作りたい【EP1-2】はじめてのDjango勉強編

#本稿は続きものです
[初回のもの書き](https://qiita.com/EconUC/items/cf1f69cfefb6c63e4c31)
[前回：はじめてのDjango勉強編1-1](https://qiita.com/EconUC/items/96fc1cd2e866f8fc72ed)
#おさらい的な
[前回](https://qiita.com/EconUC/items/96fc1cd2e866f8fc72ed)は`models.Model`を使ってモデルを作成したところで終わりました。
前回はよくわかってなかったのですっ飛ばしましたが`sqlmigrate`というコマンドで`migrate`で実行したSQLの結果を確認できるようです。

```:cmd
(Django) C:\User\mysite>python manage.py sqlmigrate polls 0001
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
```

ということでSQLで実行された内容の確認ができました。
見るにこんな感じでしょうか。

\<polls_question>

| id | question_text | pub_date |
|:-:|:-:|:-:|
| :  |  : | :  |

\<polls_choice>

| id | choice_text | votes | quetion_id |
|:-:|:-:|:-:|:-:|
 | :  | :  | :  | :  |

Pythonの`Data Frame`に似てる感じのデータベース（多分これはSQL発だから順序が逆）にデータを格納していく感じですかねー？

#APIをつかう
ここからAPIを扱っていきます。
APIってなーに？

APIはApplication Programing Interfaceの略です。そんでApplication Programing Interfaceとは何かというと、特定のアプリ(Application)をコマンドラインなどの外部(Programing)から操作することを可能にする入口(Interface)を提供するのがAPIというのが私の理解です。

その昔、私がTwitterのAPIを使ってTweetの内容を分析用に取得したときはTwitterAPIを有効にしてサポートパッケージを入れたらプログラムの中で特定のTweetデータを取得することができたりしましたんで、外部から様々な形で機能の呼び出しができるAPIというは多様な可能性を感じさせますね。
（WebページについてたりするGoogleMapなんかもAPIを用いた技術です。）
もっといい解説⇒[「API」ってつまりどんな技術？用語の意味をおさらいしよう(APIblog)](https://www.apibank.jp/contents/howto-use/use_what-api/)
　　　　　　　⇒[公式のデータベースAPIについての解説](https://docs.djangoproject.com/ja/2.2/topics/db/queries/)

今回はDjango Shellを使って諸々やっていきます。
このPythonのShellは対話シェルと呼ばれておりPythonが提供するAPIの一つです。

```:cmd
(Django) C:\User\mysite>python manage.py shell
```

このコマンドでDjangoのShellを呼び出します。
呼び出したらこの対話シェルをつかってデータベースにデータを入れていきます。

```:cmd
#クラスの呼び出し
>>> from polls.models import Choice, Question
#timezoneをdjangoのパッケージから使うので呼び出し
>>> from django.utils import timezone
#インスタンス作成
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
```

これで`Question`クラスのインスタンスの作成をしました。データベースに以下の内容で入れたということになります。

\<polls_question>

| id | question_text | pub_date |
|:-:|:-:|:-:|
| 1 |  "What's next?" | datetime.datetime(2020, 8, 8, 4, 8, 56, 186975, tzinfo=<UTC>) |

以上の内容はShell上で確認できます。

```python
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2020, 8, 8, 4, 8, 56, 186975, tzinfo=<UTC>)
```

とにかくこれで、まずは`Question`に一つデータをセットすることができました。
ここでチュートリアルでは`Question`と`Choice`のモデルに`__str__()`メソッドの追加を行っています。
`__str__()`メソッドはオブジェクトを表す様々な場面で表示してくれる文字列を決めることができます。
このDjangoのチュートリアルでは
`__str__()`メソッドの公式のドキュメントは[こちら](https://docs.python.org/ja/3/reference/datamodel.html#object.__str__)

ではチュートリアルに沿ってやっていきます。

```python:mysite/polls/models.py


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
#以下の2行を追加
    def __str__(self) -> str:
        return self.question_text


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
#以下の2行を追加
    def __str__(self) -> str:
        return self.choice_text
```

これで何が変わったのかを確認します。

```:cmd
>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet [<Question: What's new?>]>
```
ここで先ほど設定したとおりに`question_text`が表示されています。（対話シェルを開きっぱなしで`models.py`を編集していた場合は一度対話シェルを`quit()`して再度開いてからでないと変更が反映されません。）
ちなみに設定していないと

```:cmd
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
```
このような表示になります。`__str__()`を設定しておくと見やすくなるのでいいですね。

#Django adminの紹介
※チュートリアルでは`__str__()`メソッドの後にさらに対話シェルを用いていろいろとデータベースAPIの動作を試していますが、解説できないので割愛させていただきます。
Webサイトを作る上ではサイトの管理者というのは明確にしておく必要があります。
ということで管理者の作成をしていきます。

```:cmd
(Django) C:\User\mysite>python manage.py createsuperuser
Username: <管理者ユーザー名>
Email address: <管理者のメールアドレス>
Password: <管理者サイトにログインするときに使用するパスワード>
Password (again): <パスワードの確認>
Superuser created successfully.
```

これで管理者ユーザーの作成が終わったので、管理者サイトにはいってみましょう。（サーバーが`python manage.py runserver`で立ち上がっている必要があります。）
`127.0.0.1:8000/admin`をブラウザに打ってみると以下のサイトが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/a3b70d0e-f261-26df-25d0-5d499c73e973.png)


ここのUsernameとPasswordに先ほど設定したものを入れると以下の画面に入ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/7a2e8819-66fb-89ae-d40b-d9895a63001f.png)
何にも設定を入れていなくてもすでにこれだけのGUI設定画面をDjango は提供してくれます。
このサイト上で`polls`アプリの存在を反映させるために`mysite/admin.py`をいじっていきます。

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

# Register your models here.

admin.site.register(Question)
```

こんな感じで書いたら再度管理者サイトにログインしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/13420ebf-a851-a810-684e-9912eb5d60ae.png)
すると先ほどはなかったこの項目が現れました。
この項目のAddをクリックしてみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/02f5599c-7903-aa80-12c3-b45f87332b8a.png)
こんな感じで、さっき対話シェルを使って設定したQuestionの追加がなんとブラウザ上でできるようになります。サイコー！！
にしても大した設定もしてないのにここまでのブラウザ場面を出してくれるなんて本当にDjangoは手厚いですね。

#ビューを書く
Adminについては一度終わり、ここからWebページの肝であるページビューの作成に入ります。
DjangoはMTVという考え方があります。MTVはModel、Template、Viewの頭文字をとったものです。
Djangoは
　　データベースと連携をとるModel、
　　htmlファイルのTemplate、
　　ModelとTemplateを組み合わせて画面を作るView
の以上3つが中心でで成り立っているというものです。

ここまではModelを作成してきました。
ここからはViewを作っていきます。
まずは`views.py`に以下の部分を追加します。

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

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)
```

ここでチュートリアル通りにやってるとあることに気づきます。
以下の文がすでに書かれているのです。

```python:mysite/polls/views.py
def index(request):
    return HttpResponse("こんにちは！")
```
懐かしい！これは前回の最初のほうに書いたやつ！
ということは最初と同じように、この後`urls.py`にルーティングを書いてなんやかんやするんだろうなーと予想がつきます。そしてそれは正解です。
`urls.py`に`views.py`で作成した`detail`, `results`, `vote`へのルーティングを足します。

```python:mysite/polls/urls.py

urlpatterns = [

    path('', views.index, name='index'),
#以下、追加分
    path('<int:question_id>/', views.detail, name='detail'),

    path('<int:question_id>/results/', views.results, name='results'),

    path('<int:question_id>/vote/', views.vote, name='vote'),
]
```
これらのルーティングは`polls/urls.py`に書いているため、開発サーバーでいえば`127.0.0.1:8000/polls`以下のpathになります。
`path`の第一引数は`.../polls/~`としてつながるpathを示しています。
`<int:question_id>`は適当な数字を入れてよいですが、`views.py`の`detail`などに渡されます。
ということで`127.0.0.1:8000/polls/1`とすると以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/6ccffecc-b061-83b5-e7ca-8715c7bbf6e1.png)
このように`...polls/1/`の1がしっかり`detail`に渡され反映されています。`results`と`vote`も似たような感じになるので割愛。
#Templateづくり

続いてTemplateを作って`index`を改造していきます。
pollsファイルの下にtemplatesフォルダを作成します。そしてその下にさらにpollsフォルダを作ります。
ここでさらにpollsフォルダを作ります。このpollsフォルダは今後別のアプリを作ったときにDjangoがTemplateを区別できるようにするためです。
ファイルの階層構造としては
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/ae162c2d-6d8f-4e5a-da71-163776a8ba53.png)

こんな感じです。そしてこの深いほうのpollsフォルダの下にhtmlを書きます。これを間違えるとのちの作業がすべて反映されないので注意！
それどころかエラーになります。（私はtemplatesの下にpollsフォルダ作り忘れて大変なことになりました。）

```html:mysite/polls/templates/polls/index.html
{% if latest_question_list %}
    <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 %}
```
このhtmlファイル、一般的なhtmlとちょっと違くないですか？
`{%...%}`👈こいつです。こいつは「テンプレートタグ」と呼ばれるもので、if文・繰り返し文や変数のデータをhtmlで表現することができます。
あれ？なんかJavaScriptに似てね？

それではこの`index.html`を`.../polls/`で表示してくれるように``views.py`の`index`にも設定を入れます。

```python:mysite/polls/views.py
#追加
from .models import Question
#以下差し替え
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)
```
`models.py`から`Question`クラスをインポートする文の追加と既に書いてある`index`関数を以上の内容に書き換えます。
ここで登場する`render`はtemplateと使うデータを指定して画面を作ってくれます。めっちゃ有能ですこれ。
まだ使いこなすには時間がかかりそうですが、今後長い付き合いになりそうです。公式の解説は[こちら](https://docs.djangoproject.com/ja/3.0/topics/http/shortcuts/)

以上が差分です`index`は本当にこのチュートリアルの中で何度も書き直されるので、うまくいかないときはここがおかしい時が結構ありました。

#エラー送出
チュートリアルでは一度`Http404`を使ったエラー検知の方法を活用していますが、結果`get_object_or_404`を使うことになるのでここでは後者のほうのみ書きます。

```python:mysite/polls/views.py
#以下追加文
from django.shortcuts import get_object_or_404

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})
```
この差分を見ると`return`のところの返し方が`HttpResponse`から`render`に変わっています。ということで`detail.html`が新たに書かれていますが、まだこのファイルを作っていないのでこれから作っていきます。

```html:mysite/polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
```
htmlタグとDjangoのテンプレートタグ盛り盛りのコードになっていますね。
<あまりにも雑なタグ説明>
`h1`:タイトルタグ
`ul`:順序なしリスト
`li`:リストの項目

`{{ 変数 }}`:変数の展開
`{% for ... %}`:繰り返し文

#ハードコードの解消
ここもチュートリアルは寄り道しながら行っているので、ここでは最終的な差分のみ考えていこうと思います。

ここで問題となっているのは`index.html`の以下のリンクを作っている部分

```html:mysite/polls/templates/polls/index.html
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
```
この`href`のところで直接pathを打ってしまっているためコードを書き直したりするときに崩れやすくなってしまうなどのデメリットがあります。
ということで、これを`{% urls 'polls:detail' question.id %}で置き換えて、`urls.py`に`app_name = 'polls'を追加してうまいことやります。

```html:mysite/polls/templates/polls/index.html
{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}                    
        <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
```

`index.html`がこのようになっていれば、大丈夫です。
`127.0.0.1:8000/polls/`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/c51b0b7e-1e22-4c7b-cf87-9670bcfc73e9.png)
`127.0.0.1:8000/polls/1/`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402157/d643fc0b-7aef-b5ce-78bb-53c3c84a0a90.png)

まーた長くなったのでここでまた切ります。
ありがとうございます。