Django tutorial やってみた


# Writing your first Django app, part 1¶

https://docs.djangoproject.com/en/3.1/intro/tutorial01/


まず django-admin が使えなくてハマった。pip install djangoすれば入るとか言うがはいらんのじゃ。
virtualenvしておくのが必須だった。

```py
python3 -m venv venv
. venv/bin/activate
pip install django
django-admin version
```

準備できたので本家tutorialをやっていく。

https://docs.djangoproject.com/en/3.1/intro/tutorial01/

```py
$ python -m django --version
3.1.6

$ django-admin startproject mysite

$ ll
total 4.0K
drwxr-xr-x  4 user1 user1   32 2021-02-05 14:38:27 .
drwxrwxrwx 17 user1 user1 4.0K 2021-02-05 14:11:33 ..
drwxr-xr-x  3 user1 user1   37 2021-02-05 14:38:21 mysite
drwxr-xr-x  5 user1 user1  100 2021-02-05 14:34:16 venv

```

tutorialを真面目に呼んでいく。
ファイル構造を学ぶ。

```py
manage.py
mysite/
    __init__.py
    settings.py
    urls.py
    asgi.py
    wsgi.py
```

気になるところは...プロジェクト形式。

今、mysiteというプロジェクトを作ったが、これがアプリひとつ、なのか？

> mysite/settings.py: Settings/configuration for this Django project. Django settings will tell you all about how settings work.

settings.py ってこの mysite のsetting。 でも manage.py はrootに所属。
ちょっと思ってたのと違った。

asgi, wsgiはweb serverの話なのかしら。わからんので一旦パス



python manage.py runserver

死ぬほどエラーが出た。

> django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).

よしDBだな。



# Writing your first Django app, part 2¶
## DB
https://docs.djangoproject.com/en/3.1/topics/install/#database-installation


DB setupはここ
https://docs.djangoproject.com/en/3.1/ref/databases/#mysql-notes


```sql
CREATE DATABASE django CHARACTER SET utf8;
CREATE USER 'django'@'%' IDENTIFIED WITH mysql_native_password AS '***';
GRANT ALL PRIVILEGES ON *.* TO 'django'@'%' REQUIRE NONE WITH GRANT OPTION MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;
CREATE DATABASE IF NOT EXISTS `django`;
GRANT ALL PRIVILEGES ON `django`.* TO 'django'@'%';GRANT ALL PRIVILEGES ON `django\_%`.* TO 'django'@'%';

```

- settings.py

```py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/kc/my.cnf',
        },
    }
}
```

my.cnf

```
[client]
database = db.example.com
user = django
password = django
default-character-set = utf8

```


 pip install mysqlclient


全然my.cnf読んでくれないので直ガキした。

```py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',
        'USER': "django",
        'PASSWORD': "django",
        'HOST': "db.example.com",
        'PORT': 3306,
        'OPTIONS': {
            'sql_mode': 'STRICT_TRANS_TABLES',
            'charset': 'utf8mb4',
            'init_command': 'SET '
                            'character_set_connection=utf8mb4,'
                            'collation_connection=utf8_general_ci'
        }
    }
}
```



python manage.py migrate


mysql> show tables;
+----------------------------+
| Tables_in_django           |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
10 rows in set (0.07 sec)



できた！！



# runserver

python manage.py runserver

ブラウザで見れた！

http://127.0.0.1:8000/

The install worked successfully! Congratulations!






Now that your environment – a “project” – is set up, you’re set to start doing work.

ふむ。今のは "project" なのか。

mysiteの下のmysite何者。





    python manage.py startapp polls


tutorila通りにファイル作る。 polls/urls.py がなかったので作った。

興味深いのは

> Whenever Django encounters include(), it chops off whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing.

しかも `path('')` しか書いてないのに /polls で認識される。

こんなんdjango知ってないと無理だわ。直感的にできるってのは素晴らしい。


http://localhost:8000/polls/


## polls のmodelを書く
https://docs.djangoproject.com/en/3.1/intro/tutorial01/

TypeError: __init__() missing 1 required positional argument: 'on_delete'
でコケたのでこうしたら通った

```py

class Choise(models.Model):
    question = models.ForeignKey(
        Question,
        on_delete=models.CASCADE
    )
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

```

 python manage.py makemigrations polls
するとこういうファイルができる。
 polls/migrations/0001_initial.py

sqlかと思ったらpythonなんだな。

```py

    operations = [
        migrations.CreateModel(
            name='Question',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('question_text', models.CharField(max_length=200)),
                ('pub_date', models.DateTimeField(verbose_name='date published')),
            ],
        ),

```

createModelってのが、初期作成ってことかなたぶん。


そしたらsqlの・・previewができる。。本番に直接migrateたたけない場合（PaaSとか）は、このSQLをDBに直接流せばいいんだろう！

```sql
$ python manage.py sqlmigrate polls 0001
]--
-- Create model Question
--
CREATE TABLE `polls_question` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_text` varchar(200) NOT NULL, `pub_date` datetime(6) NOT NULL);
--
-- Create model Choise
--
CREATE TABLE `polls_choise` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `votes` integer NOT NULL, `question_id` integer NOT NULL);
ALTER TABLE `polls_choise` ADD CONSTRAINT `polls_choise_question_id_4bd8457d_fk_polls_question_id` FOREIGN KEY (`question_id`) REFERENCES `polls_question` (`id`);
```


これはまだDBに変更を加えない。

migrate だけを実行すると、tableができた！

```ruby
$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK
```

さっそく Choice を typo して Choise になってたので、直してみた。

models.pyを１文字直し

```py
class Choice(models.Model):
    question = models.ForeignKey(
        Question,
        on_delete=models.CASCADE
    )
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

```

```ruby
python manage.py makemigrations
python manage.py sqlmigrate polls 0002
python manage.py migrate
```

これだけで `RENAME TABLE polls_choise TO polls_choice;` が実行された！！！　便利だ！これは便利だ！！！

## manage.py shell
debugに便利そう！！
SQLが通らないときはこれで試すと良さそう。最初のdjango.setup() までだるいけど仕方ないか

```ruby
$ python manage.py shell
>>> import django
>>> django.setup()         <-----ここまで決り文句
>>>
>>> from polls.models import Question, Choice              <-----使うtable/model
>>> Question.objects.all()
<QuerySet []>

# insert
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save
<bound method Model.save of <Question: Question object (None)>>
>>> q.save()

# insertされたPKがそのままのobjectで見れる！
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2021, 3, 9, 4, 42, 28, 899072, tzinfo=<UTC>)

# updateしてみる
>>> q.question_text = "What's up"
>>> q.save()
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
```

これだと pk=1 しかわからなくて不便なので !!! ここ知りたかった！！

modelに __str__ を足す！！

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

    def __str__(self):
        return self.question_text
```

**適用するには、shellを起動し直す必要あり。**

```py
$ python manage.py shell
Python 3.7.7 (default, Jul 16 2020, 18:28:35)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import django
>>> django.setup()
>>> from polls.models import *
>>> Question.objects.all()
<QuerySet [<Question: What's up>]>       <------- テキストが見えた！
```

### queryには pk という文字が使える
table には id というカラムしかないが、id=1と同じ意味で、pkを使える。便利！

```py
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
```

### relationsのrecord作成は .save() がない

```py
>>> q.choice_set.create(choice_text='Not much', votes=0)
```

これ実行した瞬間にcommitされます。違いはなんなのかわからないし
choice_set という言葉がどこから出てきたのか意味不明。relationしてる場合のルールなんだろう、きっと。


# Writing your first Django app, part 2¶
https://docs.djangoproject.com/en/3.1/intro/tutorial02/

## admin を有効にする

tutorial通りで。model操作できるようにしたらレコードのhistory見えるのすごいな。

```
python manage.py createsuperuser
```

そしたら /login で中に入れる。

http://127.0.0.1:8000/login 

どうやるのと　→　DBにchange logが入ってた。

```sql
SELECT * FROM `django_admin_log`
```

たぶんadmin操作でしか残らないlogだな。あまり用途がわからない


fieldsets はadmin panelの見た目をいじる。columnにhtmlのheaderをつけれる。

ここらへんは使わなそうだから読み飛ばしてよさそう・・。


# Writing your first Django app, part 3¶
https://docs.djangoproject.com/en/3.1/intro/tutorial03/

html viewを作っていく。api serverとして使うからあまり不要だけど、一応基礎知識としてやっておこう

```py
urlpatterns = [
    url(r'^$', views.index, name='index'),
]
```

このregex動かない。なんだべか。と思ったら、いついれたんだっかの中身がぜんぜん違ってた。
path() じゃなくて url() を使うのね
ファイルまんま置き換えたら動いた。


jinjaは使いたくないけど一応やる。template directoryはdjagoがかってによんでくれる。
templateにわたす値は contextでtemplate.reander()に渡す。
そしたら勝手にlinkまでついた。
あー、jinjaでobjectの中使って<a>作ってるのか。

なるほど

## render shortcut
これとこれは同じ

```py
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))
```

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

こういう同じ書き方あるのが辛い。


## 404

404設定すると

DoesNotExist at /polls/13/

が

Page not found (404)

になって、

Question does not exist

を教えてくれる。

### 404 shortcut


この２つは同じ

```py
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})


def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})


```

error messageは No Question matches the given query.

 になった


viewはこんなもんでいいか


# Writing your first Django app, part 4¶
https://docs.djangoproject.com/en/3.1/intro/tutorial04/

jinjaの話なのでまるっとスキップ。

# Writing your first Django app, part 5¶

polls/tests.py を置くと

python manage.py test polls

でテストができる。面白いのが Question(pub_date=time) だけで、仮想(tmp?)のDBレコードを作ってくれる。


viewのtestは

from django.core.urlresolvers import reverse

これがこけたのでskipすることにした


# Writing your first Django app, part 6¶


css 追加方法

/polls/static/polls/styles.css を作って

{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />

で読み込む。





ふむ。ちょっとdjangoわかった