DEV Community

kaede
kaede

Posted on • Updated on

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

準備

前提、前回の復習

https://dev.to/kaede_io/docker-compose-de-django-tutorial-no-httpresponse-woque-ren-suru-67o

前回の記事で

  • Docker Compose で Django + Postgres での環境構築
  • ブラウザでのプロジェクトのトップ画面の動作確認
  • polls アプリを作成して、ルーティングの動作確認

ここまでやった。
今回はその続き Django Tutorial Part 2 をやる

https://docs.djangoproject.com/en/4.0/intro/tutorial02/


migrate で初期化

https://docs.djangoproject.com/en/4.0/intro/tutorial02/#creating-models

docker-compose run web python manage.py migrate

Starting rest0406_db_1 ... done
Creating rest0406_web_run ... done
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
Enter fullscreen mode Exit fullscreen mode

まずはこれで DB の初期化をする。
これで新しく DB を作る準備ができると推測する。



モデルファイルを作成して DB テーブルを作る

Question テーブルのモデルファイルを書く

次に 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)
Enter fullscreen mode Exit fullscreen mode

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

このふたつ、 Blitz のチュートリアルでも見たな...

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

https://docs.djangoproject.com/en/4.0/intro/tutorial02/#activating-models

これを読み込むためには projectName/settings.py
に追加の設定を書き込む必要がある。

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    ...
]
Enter fullscreen mode Exit fullscreen mode

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.

makemigrations を動かすことで
Django に「モデルが変わったよ!」と伝えているらしい。

docker-compose run web \
python manage.py makemigrations polls
Enter fullscreen mode Exit fullscreen mode

makemigrations コマンドを打つ。

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
Enter fullscreen mode Exit fullscreen mode

これで初期化の準備ファイル

# Generated by Django 3.2.12 on 2022-04-09 08:24

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Question',
            fields=[
                ('id', models.BigAutoField(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')),
            ],
        ),
        migrations.CreateModel(
            name='Choice',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('choice_text', models.CharField(max_length=200)),
                ('votes', models.IntegerField(default=0)),
                ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
            ],
        ),
    ]
Enter fullscreen mode Exit fullscreen mode

0001_initial.py が作成された。

このファイルを指定して sqlmigrate する。


sqlmigrate で SQL を発行する

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;
Enter fullscreen mode Exit fullscreen mode

これで sql を発行する


migrate で sql を実行する

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
Enter fullscreen mode Exit fullscreen mode

これで SQL を実行し、テーブルを作成できたと解釈する。



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

shell コマンドでインタラクティブモードを起動する

シェルモードで、実際にテーブルが作成されているのか確認する。

https://docs.djangoproject.com/en/4.0/intro/tutorial02/#playing-with-the-api

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)
>>> 
Enter fullscreen mode Exit fullscreen mode

インタラクティブ コンソールが起動する。


Questions の中身を objects.all() で確認する

>>> from polls.models import Choice, Question

Question.objects.all()
<QuerySet []>
Enter fullscreen mode Exit fullscreen mode

migrate で作成した polls アプリのモデルの
Choice, Questions のテーブルをシェルでインポートして
Question の objects.all() で中身を見る。

すると空のクエリセットが帰ってくる。
クエリセットは JSON に変換する前の状態だと解釈する


Question に question_text と pub_date のデータを入れる

from django.utils import timezone
Enter fullscreen mode Exit fullscreen mode

pub_date で使うので timezone のライブラリをインポートする

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

Question モデルの中に

  • question_text という変数名で What's new? というテキスト
  • pub_date という変数名で timezone.now() の現在の時間 これらを入れ、それを q という変数に入れる。
>>> q
<Question: Question object (None)>
>>> q.question_text
"What's new?"
Enter fullscreen mode Exit fullscreen mode

変数の q そのものに アクセスしても Object (None) になってしまうが
q.question_text にアクセスすると中身の文字列が返ってくる

>>> q.id
>>>
Enter fullscreen mode Exit fullscreen mode

id は最初はない。

>>> q.save()
>>> q.id
1
Enter fullscreen mode Exit fullscreen mode

save() すると id が決まる。

>>> q.pub_date

datetime.datetime(2021, 12, 19, 13, 17, 26, 609289, tzinfo=<UTC>)
Enter fullscreen mode Exit fullscreen mode

pub_date にアクセスすると 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
Enter fullscreen mode Exit fullscreen mode

q に objects.all() でアクセスしても中身は見えない。

>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
Enter fullscreen mode Exit fullscreen mode

Question モデルに直接 objects.all() することで中身を見れる

しかしこれだと中身の詳細出てこないので、
objects.all() をしただけでモデルの詳細まで出るようにする。


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

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
      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)
    def __str__(self):
        return self.choice_text
Enter fullscreen mode Exit fullscreen mode

polls/models.py の Question と Choice のクラスに
__str__ という関数を追加する。

>>> exit()
kaede0902@rooter rest0406 % 

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)
Enter fullscreen mode Exit fullscreen mode

反映させるためにシェルを再起動

>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet [<Question: How Are You?>]>
Enter fullscreen mode Exit fullscreen mode

これで 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?>]>
Enter fullscreen mode Exit fullscreen mode

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?>
Enter fullscreen mode Exit fullscreen mode

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


Discussion (0)