Djangoチュートリアル備忘録②

#はじめに
この記事は[はじめての Django アプリ作成、その2](https://docs.djangoproject.com/ja/3.1/intro/tutorial02/#playing-with-the-api)の備忘録です.
その１はこちら→[Djangoチュートリアル備忘録①](https://qiita.com/surai_uni/items/8271dfdb16304a367186)

#データベースの設定
データベースの設定はmysite/setting.pyから操作することができます.

デフォルトではSQLiteを使用するようになっていますが,本番環境で使う場合にはPostgreSQLなどのスケーラブルなデータベースを使用することが推奨されます.

今回はチュートリアルということでSQLiteを使っていくので変更は不要です.
なおここでTIME_ZONEに自分のタイムゾーンを設定します.デフォルトは世界標準時で日本の場合はAsia/Tokyoと書きます.

##INSTALLED_APPSについて
setting.py内のINSTALLED_APPS内にはデフォルトで以下のアプリケーションが入っています.

+ django.contrib.admin・・・管理(admin)サイト
+ django.contrib.auth・・・認証システム
+ django.contrib.contenttypes・・・コンテンツタイプフレームワーク
+ django.contrib.sessions・・・セッションフレームワーク
+ django.contrib.messages・・・メッセージフレームワーク
+ django.contrib.staticfiles・・・静的ファイルの管理フレームワーク




##ほかのデータベースを使う場合
後から書きます
                                                   
#データベースの作成
##テーブルの作成
まず最初にテーブルを作成する必要があります.下記コマンドを実行します

```shell
$ python manage.py migrate
```
migrateコマンドはINSTALL_APPSの設定を参照して,mysite/setting.pyファイルのデータベース設定に従って必要なすべてのデータベースのテーブルを作成します.

##モデルの作成
DjangoはDRY則に沿っています. DRYとは同じことを繰り返さないという意味であり,Djangoにおいては冗長性は悪であり,正規化こそ善なのです.
よってDjangoではただ一つの場所でデータモデルを定義し,そこから自動的にいろいろなものへ派生させます.

pollsアプリケーションでは**Question**と**Choice**の二つのモデルを作成します.
![Django-ER.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269896/261c87f5-64a7-fc27-1525-272b1755745b.png)

上記の図のようになるのでそれをPythonのコードで表すと下記のようになります.

```polls/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)
```

各変数名は機械可読なフィールド名となっており,これらはデータベースの列の名前としても使います.また,Fieldの最初の引数にオプションとして人間可読なフィールド名を指定することもできます.上記のコードではpub_dateのFieldに人間可読なフィールド名を設定しています.

Choiceのquestion内のmodels.ForeignKey()の引数であるon_delete=models.CASCADEは外部キーとして参照している元々のデータが削除された場合にこのデータも削除することを表しています.

##モデルを有効化する
先ほど書いたモデルをプロジェクトに認識させるためにはpollsアプリケーションをインストールしたことをプロジェクトに教えてあげる必要があります.

アプリケーションをプロジェクトに含めるには,構成クラスへの参照をINSTALL_APPSに追加する必要があります. 追加したものは下記のようになります.

```mysite/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',
]
```
この設定が完了したらこの変更のマイグレーションを作成します.

```shell
$ python manage.py makemigrations polls
```

**makemigrations**を実行することで,Djangoにモデルに変更があったことを伝え,その変更をマイグレーションの形で保存することができます.

その後下記コマンドでモデルのテーブルをデータベースに作成します.

```shell
$ python manage.py migrate
```
これでデータベース上にテーブルを作成することができました.

#APIで遊んでみる
Djangoが提供するAPIを利用するためPythonシェルを起動します.

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

shellが起動したらデータベースAPIを試していきます.

```python
>>> from polls.models import Choice, Question

>>>Question.objects.all()
<QuerySet[]>

>>>from django.utils import timezone
>>>q= Question(question_text="What's new?",pub_date=timezone.now())

>>>q.save()

>>>q.id

>>>q.question_text
"Whats new?"
>>>q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

>>>q.question_text = "what's up?"
>>>q.save()

>>>Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

```
最終行の結果を見ると<Question: Question object (1)>となっており,このままでは何が格納されているのかわかりません.
これを修正するために__str__()メソッドをQuestionとChoiceの両方に追加します.

```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
```
この機能の追加はadminで管理する際の利便性も高まるため重要です.

モデルクラスにクラスメソッドを追加してみます.

```polls/models.py
import datetime #←NEW!!

from django.db import models   
from django.utils import timezone #←NEW!!


class Question(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
```
上記のwas_published_recently()は一日以内に更新されたデータかどうかを判別する関数です.

では,変更を保存してもう一度PythonShellを起動します.

```python
>>>from polls.models import Choice,Question

>>>Question.objects.all() #文字列として表示されるようになっているか確認
#<QuerySet [<Question: What's up?>]>

>>> Question.objects.filter(id=1) 
#<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
#<QuerySet [<Question: What's up?>]>

>>>from django.utils import timezone
>>>current_year = timezone.now().year
>>>Question.objects.get(pub_date__year=current_year)
#<Question: What's up?>

>>>Question.objects.get(pk=1) #プライマリキーが1のものを検索
#<Question: What's up?>

>>>q.was_published_recently() #1日以内に投稿されたものか確認
True

>>> q = Question.objects.get(pk=1)

>>>q.choice_set.all()

>>>q.choice_set.create(choice_text="Not much", votes=0)
>>>q.choice_set.create(choice_text="The sky", votes=0)
>>> c=q.choice_set.create(choice_text="Just hacking again", votes=0)

>>>c.question
#<Question: What's up?>

>>>q.choice_set.all()
#<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>>q.choice_set.count()
3

>>> Choice.objects.filter(question__pub_date__year=current_year)
#<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
```

#Django Admin
Django Adminはページの変更や削除のための管理サイトの生成を自動化してくれる機能です.

##管理ユーザーを作成する
まず最初にadminサイトにアクセスするためのユーザーを作成する必要があります.
そのために下記のコマンドを実行します.

```shell
$ python manage.py createsuperuser
```
すると下記のようなエラーが出る場合があります.

```shell
Superuser creation skipped due to not running in a TTY. You can run manage.py createsuperuser in your project to create one manually. 
```

その場合は下記のコマンドを実行してください.

```shell
$ winpty python manage.py createsuperuser
```

すると入力待ち状態になるので
好きなユーザー名を入力
↓
Emailを設定
↓
パスワードの設定
まで行ってください.

```shell
Username: (username)

Email address: hogehoge@example.com

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

##開発サーバーの起動
Django adminサイトはデフォルトで有効化されているため,開発サーバーを起動するだけでアクセスすることができます.

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

次に http://127.0.0.1:8000/admin/ にアクセスします.
すると,adminのログイン画面が表示されるはずです.

adminサイトに入るとGroupsとUsersが表示されていますがpollsアプリの管理用画面が存在しません.

admin上で編集できるようにするためにはpolls/admin.pyを編集する必要があります.

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

from .models import Question

admin.site.register(Question)
```
上記の設定を行うとadminサイトにQuestionsの設定が表示されます.

#おわりに
今回はデータベースとその管理のための設定を行いました.
その3も近々追加します.