Django Tutorial Part2 -- Model から Table を作って shell mode で Table の中身を動かす

前回

前回の記事で Docker Compose で環境構築して

Django Tutorial の最初までやった

今回はその続きをやる

part 2

モデルを作る

docker-compose run web python manage.py migrate

まずはこれで DB の初期化をする必要がある

次に DB テーブルを作るためのモデルのファイルを書く。

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)

Question とそれに関連する Choice のデータベースを作る

これ Blitz のチュートリアルでも見たぞ

モデルを読み込んで有効化する

これを読み込むためには 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',
]

polls/ の一つ上のディレクトリの settings.py の

INSTALLED_APPS に 'polls.apps.PollsConfig', を追加する

makemigrations で 初期化の準備ファイルを作る

By running makemigrations, you’re telling Django that you’ve made some changes to your models (in this case, you’ve made new ones) and that you’d like the changes to be stored as a migration.

docker-compose run web \
python manage.py makemigrations polls

polls から migrate する

docker-compose run web \
python manage.py makemigrations polls
Creating dockerdjango_web_run ... done
Migrations for 'polls':
  polls/migrations/0001_initial.py
    - Create model Question
    - Create model Choice

初期化の準備ファイルが作られた

sqlmigrate で makemigrations で作った init ファイルを

migrate をする

docker-compose run web \
python manage.py sqlmigrate polls 0001

[+] Running 1/0
 ⠿ Container dockerdjango-db-1  Running                                                                                                    0.0s
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" bigserial NOT NULL PRIMARY KEY, "question_text" varchar(200) NOT NULL, "pub_date" timestamp with time zone NOT NULL);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" bigserial NOT NULL PRIMARY KEY, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" bigint NOT NULL);
ALTER TABLE "polls_choice" ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id" FOREIGN KEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;

これで sql を発行する

migrate で sql を実行する

docker-compose run web \
python manage.py migrate

これを実行して

docker-compose run web \
python manage.py migrate
[+] Running 1/0
 ⠿ Container dockerdjango-db-1  Running                                                                                                    0.0s
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK

反映された

shell でインタラクティブモードでテーブルの中身を確認する

docker-compose run web \
python manage.py shell

これを実行すると

docker-compose run web \
python manage.py shell
[+] Running 1/0
 ⠿ Container dockerdjango-db-1  Running                                                                                                    0.0s
Python 3.10.0 (default, Oct 16 2021, 10:05:15) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

インタラクティブモードになる

>>> from polls.models import Choice, Question

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

作った投票アプリのモデルから、質問と選択肢を持ってきて

全部を引っ張ってくると、何も入っていない

timezone の形でデータを入れて、save して Question.object.all で中身を見る。

from django.utils import timezone

timezone のライブラリを持ってきて

q = Question(question_text="What's new?", pub_date=timezone.now())

q という一時変数に 質問文のテキストと、作成日の日付に現在の日付を入れる

>>> q
<Question: Question object (None)>
>>> q.question_text
"What's new?"

>>> q.id
>>>

中身のカラムを指定するとみれる、今は id がないが

>>> q.save()
>>> q.id
1

保存すると id が出る

q.pub_date
datetime.datetime(2021, 12, 19, 13, 17, 26, 609289, tzinfo=<UTC>)

datetime で保存されている

>>> q.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python3.10/site-packages/django/db/models/manager.py", line 179, in __get__
    raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__)
AttributeError: Manager isn't accessible via Question instances

中身を全て見ることはこの段階ではできない

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

しかしこれだと中身が出てこないので、
もう少し工夫する必要がある

str を追加して text の中身を見えるようにする

docker-compose run web \
python manage.py shell
[+] Running 1/0
 ⠿ Container dockerdjango-db-1  Running                                                                                                    0.0s
Python 3.10.0 (default, Oct 16 2021, 10:05:15) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> Question.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'Question' is not defined
>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet [<Question: How Are You?>]>

Question の中身を見る時に、object の中身の text が表示されるようになった

条件でクエリする

>>> Question.objects.filter(id=1)
<QuerySet [<Question: How Are You?>]>
>>> Question.objects.filter(id=2)
<QuerySet []>

>>> Question.objects.filter(question_text__startswith='H')
<QuerySet [<Question: How Are You?>]>

filter, startswith,

で検索することができるし

>>> Question.objects.get(id=1)
<Question: How Are You?>
>>> Question.objects.get(id=0)
...
polls.models.Question.DoesNotExist: Question matching query does not exist.

>>> Question.objects.get(pk=1)
<Question: How Are You?>

id, primary key で指定して呼び出すこともできる

Question id 1 の下に .create(column='hoge') でChoice を作る

>>> q = Question.objects.get(pk=1)
>>> q.choice_set.all()
<QuerySet []>

子供テーブルは parent.children_set.all() で見れるらしい。

>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>]>

それに .create(column_name='text', column_name2=0)
のようにつけると追加できる!

.count() で数える

>>> q.choice_set.count()
1

.count で数を数えられて

.delete() で削除する

>>> c = q.choice_set.filter(choice_text__startswith='Not')
>>> c
<QuerySet [<Choice: Not much>]>
>>> c.delete
<bound method QuerySet.delete of <QuerySet [<Choice: Not much>]>>
>>> c.delete()
(1, {'polls.Choice': 1})
>>> q.choice_set.all()
<QuerySet []>
>>>

取得して .delete() をかけると削除できた!

最近投稿されたか否かの関数を作る

def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

現時点から1日経っていない投稿かを判断する

次にやること

admin を作る

55