既存データベースをDjangoで利用しようとしたら簡単すぎて拍子抜けた話

#目的
先日の記事を読んでくれた知人から
「じゃあ既に運用しているDBがあったとして、後からDjangoをくっつけてくることはできるのか？」
なんていう勝手なリクエストがあったので、こちらも検証してみました。

結論から言ってしまうと、できました。しかもあっさり。
まあ当たり前ですが、対応しているデータベース製品とバージョンがある程度限定されるようです。
<a href="https://docs.djangoproject.com/en/2.1/howto/legacy-databases/">この辺</a>が参考になると思います。

#やり方
前回の記事のアプリケーションを再度利用しますが、データベース側のテーブルは削除しておきます。これで、Djangoコードはあるけれどもデータベースがまだ用意されていない、という状態になっています。

## 現状確認
settings.pyには設定が書いてあるものの、データベース側の準備ができていないので
この状態でサーバを立ち上げようとするとこんな感じになります。
まあそりゃそうですね。

```shell
> python manage.py runserver

~中略~
django.db.utils.OperationalError: FATAL:  database "webshopping" does not exist
```

## データベースを準備する
既存のデータベースと見立てるために、適当なデータベースとテーブルを用意します。

```shell
> psql -U postgres -W
ユーザ postgresのパスワード:********

postgres=# CREATE DATABASE newshopping;
CREATE DATABASE

postgres=# \c newshopping;
newshopping=# CREATE TABLE items(
                item_name TEXT PRIMARY KEY,
                price INTEGER
              );
CREATE TABLE
```

更に、適当なデータを放り込みます。

```shell
postgres=# INSERT INTO items VALUES('割れやすい窓ガラス', 50000);
INSERT 0 1
postgres=# INSERT INTO items VALUES('割れにくい窓ガラス', 99000);
INSERT 0 1

newshopping=# SELECT * FROM items;
     item_name      | price
--------------------+-------
 割れやすい窓ガラス | 50000
 割れにくい窓ガラス | 99000

(2 行)
```

というわけで、データはしっかりと挿入されているようです。

## settings.pyの確認
データベース製品を変える場合は、settings.pyを変える必要があります。
ENGINE部分の書き方に関しては、<a href="https://docs.djangoproject.com/en/2.2/ref/settings/#s-engine">このあたり</a>を参考にしてください。postgresqlの場合は以下でOKです。
NAME部分は、データベースの識別名です。先程、newshoppingという名前で作成していたのでそのとおりに書いていきます。
その他のパラメータに関しては、特別な事はありません。アクセスするユーザ名やパスワード、ホスト名を書けばOKです。

```python:settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'newshopping',
        'USER': 'your_db_username',
        'PASSWORD': 'your_db_password',
        'HOST': 'localhost',
        'PORT': '',
    }
}
```

## djangoのツールを使って連携させる
冒頭で貼った<a href="https://docs.djangoproject.com/en/2.1/howto/legacy-databases/">こちら</a>のURLに詳しい使い方が書いてありますが、既存データベースとの調整を行ってくれるツールがあります。
起動の仕方はこんな感じです。

```shell
> python manage.py inspectdb

class Items(models.Model):
    item_name = models.TextField(primary_key=True)
    price = models.IntegerField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'items'
```

settings.pyの設定がうまくいっていれば、上記のようにModelに該当するクラスのコードが返ってきます。
これを自分のModels.pyに貼り付けるか、あるいは以下のようにして既存のModels.pyへ直接書き込むと良いでしょう。

```shell
> python manage.py inspectdb >> models.pyへのパス/models.py
```
「>」にしてしまうと上書きされてしまうので、「>>」にすることをオススメします。
あとは、不要になった既存の記述はコメントアウト等しておいたほうが良いです。

models.pyを見てみると...

```python:models.py
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey has `on_delete` set to the desired behavior.
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models


class Items(models.Model):
    item_name = models.TextField(primary_key=True)
    price = models.IntegerField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'items'

```
無事にクラス定義が追加されています！

## 既存コードとの最終調整
さて、私はテーブル名の付け方をミスってしまいました。
コードは「Item」クラスで通していたのですが、自動生成されたModelのクラスは「Items」になっています。これは、データベース内のテーブル名が「items」だからですね。

でも大丈夫です。所詮はクラス定義なので、このクラス名は変えても問題有りません。
というわけで、クラス定義の部分をちょちょっと弄りました。

```python:models.py
# ~略~
class Item(models.Model):
# ~略~
```

## 動作確認
さて、サーバを起動して動作確認をしてみます。

```shell
> python manage.py runserver
```

全く問題なさそうです！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/99bee721-259e-e718-9563-4b831b29bd44.png)

#結論
* 既存DBをDjangoで使いたいならpython manage.py inspectdbを使うべし！一瞬で終わるから！