Django3をインストールしてローカルのpostgreSQLと繋げる

スポンサーリンク

[実行環境]
Win10
python 3.7.7(64bit)
pip 20.1.1
pipenv 2020.6.2
Django 3.0.7
postgreSQL 12.3

Djangoのインストール

まずは、仮想環境を作成して最新のDjangoをインストール。
はじめての Django アプリ作成、その 1 | Django ドキュメント | Django

> mkdir 20200619_diango_ver_3
> cd 20200619_diango_ver_3
> pipenv install django
Creating a virtualenv for this project…
...略...

pipenvのシェルを有効にして、

> pipenv shell

プロジェクトの作成。

(.venv) > django-admin startproject mysite

開発用サーバーの起動(いくつか警告が出ますが無視します)

(.venv) > cd mysite
(.venv) > python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
...略...

http://127.0.0.1:8000/にアクセスすると、"Congratulations!"と表記されたロケットの離陸ページが出ます。
f:id:always_amefuri:20200619112821p:plain

続いてアプリケーションを作ります。
Djangoにはプロジェクトとアプリケーションという概念があります。

プロジェクトとアプリケーション

プロジェクトとアプリの違いは何でしょうか? アプリとは、ウェブログシステム、公的記録のデータベース、小規模な投票アプリなど、何かを行う Web アプリケーションです。プロジェクトは、特定のウェブサイトの構成とアプリのコレクションです。プロジェクトには複数のアプリを含めることができます。 アプリは複数のプロジェクトに存在できます。

はじめての Django アプリ作成、その 1 | Django ドキュメント | Djangoより

bookはただのアプリケーション名なので任意です。

(.venv) > python manage.py startapp book

アプリケーションを作成したら、mysite/book/views.pyを以下のようにして、

from django.http import HttpResponse

def index(request):
	return HttpResponse("Hello, world!")

mysite/book/urls.pyを作成してviewとurlを紐つけます。

from django.urls import path
from . import views

urlpatterns = [
	path('', views.index, name='index'),
]

mysite/mysite/urls.pyを変更してbookをルーティング。

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('book/', include('book.urls')),
    path('admin/', admin.site.urls),
]

もう一度、サーバーを起動して、

(.venv) > python manage.py runserver

http://127.0.0.1:8000/book/にアクセスして「Hello, world!」が表示されたらOK。

DBの設計

ローカルにすでにtestdbというpostgreSQLの空っぽDBがあるとします。
はじめての Django アプリ作成、その2 | Django ドキュメント | Django

最初にpythonのPostgreSQLドライバpsycopg2をインストールしておきます。
ないとエラーが出ました。

(.venv) > pipenv install psycopg2
Installing psycopg2…
...略...

mysite/mysite/settings.pyを開けると、デフォルトではDBはsqliteが選ばれていて、

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }

それを以下のように変更します。
特にオンラインで使用する予定がないためパスなども直書きしてますが、herokuなどにデプロイする場合は隠蔽する必要があります。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'testdb',
        'USER': 'hogehoge',
        'PASSWORD': 'hogehoge',
        'HOST': 'localhost',
    }
}

ENGINEの項目は、以前は'django.db.backends.postgresql_psycopg2'という名前を使っていたようですが、要は同じだそうです。
python - What is the difference between postgres and postgresql_psycopg2 as a database engine for django? - Stack Overflow

Django内で使うフレームワークのためにDBに予め指定のテーブルがいるらしく、そのためにマイグレーションを実行します。

(.venv) > python manage.py migrate
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 sessions.0001_initial... OK

ローカルのDBを確認すると、Django関係のテーブルが色々と挿入されています。

testdb=# \dt
                      リレーション一覧
 スキーマ |            名前            |    型    |  所有者
----------+----------------------------+----------+----------
 public   | auth_group                 | テーブル | hogehoge
 public   | auth_group_permissions     | テーブル | hogehoge
 public   | auth_permission            | テーブル | hogehoge
 public   | auth_user                  | テーブル | hogehoge
 public   | auth_user_groups           | テーブル | hogehoge
 public   | auth_user_user_permissions | テーブル | hogehoge
 public   | django_admin_log           | テーブル | hogehoge
 public   | django_content_type        | テーブル | hogehoge
 public   | django_migrations          | テーブル | hogehoge
 public   | django_session             | テーブル | hogehoge

モデルの作成

自作アプリケーションbookをプロジェクトに含めます。
settings.pyのINSTALLED_APPSに'book.apps.BookConfig'を追加します。

INSTALLED_APPS = [
    'book.apps.BookConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

以前は'book'とアプリ名だけの追加でしたが、これもバージョンが上がって変わったようです。
python - Two ways of including an app in INSTALLED_APPS - Stack Overflow

テーブル構造は、Auhor(作者)とBook(本)という一対多の関係を考えてみます。
「一対一」「一対多」「多対多」のリレーションを分かりやすく説明する - akiyoko blog
mysite/book/models.pyを編集します。
Django 3.0 のモデルフィールドリファレンス一覧まとめ - Qiita

from django.db import models

class Author(models.Model):
	name = models.CharField(verbose_name='名前', max_length=100)
	age = models.PositiveIntegerField(verbose_name='年齢')
	#__str__は管理画面で表示される無味乾燥なオブジェクト表記を作者名に改める
	def __str__(self):
		return self.name

class Book(models.Model):
	#ForeignKeyは作者と著作本の、一対多のリレーションを作る
	author = models.ForeignKey(Author, verbose_name='作者', on_delete=models.CASCADE)
	name = models.CharField(verbose_name='名前', max_length=100)
	release_date = models.DateTimeField(verbose_name='発売日')
	def __str__(self):
		return self.name

モデルに変更を加えたら、それをmakemigrationsでDjangoに伝えます。

(.venv) > python manage.py makemigrations book
Migrations for 'book':
  book\migrations\0001_initial.py
    - Create model Author
    - Create model Book

実行すると、mysite/book/migrationsフォルダの中に、0001_initial.pyというファイルができます。

# Generated by Django 3.0.7 on 2020-06-23 02:41

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


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Author',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100, verbose_name='名前')),
                ('age', models.PositiveIntegerField(verbose_name='年齢')),
            ],
        ),
        migrations.CreateModel(
            name='Book',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100, verbose_name='名前')),
                ('release_date', models.DateTimeField(verbose_name='発売日')),
                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='book.Author', verbose_name='作者')),
            ],
        ),
    ]

モデルを編集してmakemigrationsするごとにマイグレーションファイルが作成され、変更履歴が追えるようになっています。
migrateを実行すると、それらはCREATE TABLE文などのpostgreSQLのコマンドとして実行されます。

(.venv) > python manage.py sqlmigrate book 0001
BEGIN;
--
-- Create model Author
--
CREATE TABLE "book_author" ("id" serial NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "age" integer NOT NULL CHECK ("age" >= 0));
--
-- Create model Book
--
CREATE TABLE "book_book" ("id" serial NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "release_date" timestamp with time zone NOT NULL, "author_id" integer NOT NULL);
ALTER TABLE "book_book" ADD CONSTRAINT "book_book_author_id_40846805_fk_book_author_id" FOREIGN KEY ("author_id") REFERENCES "book_author" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "book_book_author_id_40846805" ON "book_book" ("author_id");
COMMIT;

migrateすると、DBにAuthorとBookというテーブルが挿入されました。

(.venv) > python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, book, contenttypes, sessions
Running migrations:
  Applying book.0001_initial... OK
testdb=# \dt
                      リレーション一覧
 スキーマ |            名前            |    型    |  所有者
----------+----------------------------+----------+----------
 public   | auth_group                 | テーブル | hogehoge
 public   | auth_group_permissions     | テーブル | hogehoge
 public   | auth_permission            | テーブル | hogehoge
 public   | auth_user                  | テーブル | hogehoge
 public   | auth_user_groups           | テーブル | hogehoge
 public   | auth_user_user_permissions | テーブル | hogehoge
 public   | book_author                | テーブル | hogehoge
 public   | book_book                  | テーブル | hogehoge
 public   | django_admin_log           | テーブル | hogehoge
 public   | django_content_type        | テーブル | hogehoge
 public   | django_migrations          | テーブル | hogehoge
 public   | django_session             | テーブル | hogehoge

DBを設計する場合、postgreSQLのコマンドを覚えるよりも、Djangoのモデルから作る方がずっと楽ですね。

管理画面からDBにアクセスする

adminサイトにログインするためのユーザーを作成します。

(.venv) > python manage.py createsuperuser
ユーザー名 (leave blank to use 'XXX'): testuser
メールアドレス: sample@example.com
Password:
Password (again):
Superuser created successfully.

runserverして、http://127.0.0.1:8000/admin/にアクセスします。
f:id:always_amefuri:20200623102416p:plain

先ほど作成したユーザーで管理画面にログインすることができます。
f:id:always_amefuri:20200623115133p:plain

管理画面にAuthorとBookが表示されていないので追加します。
mysite/book/admin.pyを以下のように編集します。

from django.contrib import admin
from .models import Author, Book

admin.site.register(Author)
admin.site.register(Book)

再度管理画面にアクセスすると、無事表示されました。自動的に複数形になるようです。
f:id:always_amefuri:20200623115142p:plain

管理画面から作家レコードを追加してみます。
f:id:always_amefuri:20200623103836p:plain

追加できました。
f:id:always_amefuri:20200623104326p:plain

ちなみに、モデルで__str__メソッドを指定しないと、以下のような味気ない表記になります。
f:id:always_amefuri:20200623103902p:plain

著作も追加してみます。
作者の箇所が、一対多を表すリレーションです。時刻などは適当。
f:id:always_amefuri:20200623115848p:plain

うまくいったようです。
f:id:always_amefuri:20200623120200p:plain