既存のDjangoのアプリケーション名を変更する

## これは何？
普段Djangoで開発していて、たまに思うことがあるかもしれません。

`今問題なく動いているんだけど、アプリケーション名がちょっと気に食わない。`

試しにただフォルダ名を変更してみると、不要なテーブルが出来てしまい、既存のデータも使えません。

「既存のテーブルに入っているデータはそのまま使いつつ、アプリケーション名を変更したい」という時に何をすればいいのか

色々な方法があると思いますが、そのうちの一つとしてまとめました。


## 結論
[stack overflow/How to change the name of a Django app?](https://stackoverflow.com/questions/8408046/how-to-change-the-name-of-a-django-app)
ここに書いている通りです。

1. フォルダ名を変更する
2. AppConfigを変更, settingsの`INSTALLED_APPS`を変更する
3. アプリケーション名変更前のテーブルを新しいアプリケーション名に合わせてrenameする
4. django_content_typeとdjango_migrationsを書き換える

の4ステップで既存のコードやレコードはそのままにアプリケーション名を変更することが出来ました。

### なぜ上記のステップが必要なのか。
なぜフォルダ名を変更しただけでは想定通りの動きをしてくれないのかを簡単に書きます。

Djangoのmigrationは、`django_migrations`テーブルで管理されています。([Sequel Pro](https://www.sequelpro.com/)を使っています)
`↓django_migrations`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192104/0b60a985-e5ab-4e45-1091-9474bd5a2174.png)

appカラムは`settings.py`の`INSTALLED_APPS`、
nameカラムは各アプリケーションの`migrations`内のファイル名が入っています。


また、Modelは`django_content_type`テーブルで管理されています。
`↓django_content_type`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192104/0764f303-ba94-7aab-4086-d472a87a7412.png)

app_labelカラムは同様に`settings.py`の`INSTALLED_APPS`、
modelカラムは各アプリケーション内の`models.py`のモデル名が入っています。

フォルダ名を変更したとしても、これらがそのままだと、Djangoから`新しい全く違うアプリケーションが作成された`と認識されてしまいます。

上記の3, 4のステップを踏むことで、Djangoにrenameしたアプリケーションを元からあるものだと認識してもらう必要があります。

下では手を動かした過程を書いています。
興味のある方は是非読んでみてください。

---
## 前提
作った `blogs app` を `vlogs app` に renameしたくなったとします。

`blogs app`を作ります。

1: Django projectを作成

```shell
$ django-admin startproject my-site
```

2: blogs app を作成

```shell
$ python manage.py startapp blogs
```

3: PostModelを作成

`blogs/models.py`

```python
from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=255)

```

4: migrationを走らせる
`my-site/settings.py`

```
INSTALLED_APPS = [
    'blogs.apps.BlogsConfig',
    ...
]
```

```shell
$ python manage.py migrate
```

こうするとこのようなテーブルが作られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192104/a07f1b0a-576d-6072-f1a8-edd359a3e57a.png)

ここから `blogs`を`vlogs`に変更します。

## 1.フォルダ名を変更する
試しにフォルダ名を変更するだけだとどうなるのかを紹介します。

`/blogs` → `/vlogs` とします。
この状態で

```shell
$ python manage.py migrate
```

とするともちろん
`ModuleNotFoundError: No module named 'blogs'`
となります。

では次です。

## 2. AppConfigを変更, settingsの`INSTALLED_APPS`を変更する
1で怒られてしまったように、`INSTALLED_APPS`を編集しなければいけません。

`vlogs/apps.py`

```python
from django.apps import AppConfig


class VlogsConfig(AppConfig):
    name = 'vlogs'

```

`settings.py`

```python

INSTALLED_APPS = [
    'vlogs.apps.VlogsConfig',
    ...
]
```

これでDjangoが`vlogs app`を認識してくれるようになるはずです。
`showmigrations`を実行します

``` shell
$ python manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
vlogs
 [ ] 0001_initial
```

認識されていますね。`migrate`します。

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

このようにうまく実行されますが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192104/8d0d52b5-62ac-be95-85bf-bba478d0a30b.png)
`vlogs_post`と`blogs_post`が出来てしまいます。
理想は`vlogs_post`だけになっていることです。
では`blogs_post`を`vlogs_post`に変更します。

## 3. アプリケーション名変更前のテーブルを新しいアプリケーション名に合わせてrenameする
PostModelから作られるテーブルは`blogs app`を`vlogs app`にrenameすることで`vlogs_post`になって欲しいです。
テーブル名を変更します。

```mysql
mysql> ALTER TABLE blogs_post RENAME TO vlogs_post;
```

## 4. django_content_typeとdjango_migrationsを書き換える
今までblogsとして扱っていたアプリケーションを今後はvlogsとして扱ってもらうために
`django_content_type`と`django_migrations`を書き換えます。

```mysql
mysql> UPDATE django_content_type SET app_label='vlogs' WHERE app_label='blogs'
mysql> UPDATE django_migrations SET app='vlogs' WHERE app='blogs'
```

## 結果
では、問題ないかどうかを確認します。
確認の方法としては

1: Modelに変更を加えずにmigrateを実行した時に何も起こらないこと

```shell
$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, vlogs
Running migrations:
  No migrations to apply.
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192104/6386668a-87da-0c9a-f618-90097421b060.png)
問題なさそうですね。

2: Modelに変更を加えた時に続きからmigrationが作成されること
Postモデルにcontentカラムを追加します。
`vlogs/models.py`

```python

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.CharField(max_length=255, null=True)

```

```shell
$ python manage.py makemigrations
Migrations for 'vlogs':
  vlogs/migrations/0002_post_content.py
    - Add field content to post

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, vlogs
Running migrations:
  Applying vlogs.0002_post_content... OK
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192104/b46fda06-be3d-2692-6004-bf79d238e603.png)

このように`vlogs/migrations`配下に`0002_post_content.py`が続きとして作成されcontentカラムが問題なく追加されました。


## まとめ
Djangoのmigrationが何を見て動いているのかをなんとなく理解するのに良いお題だと思いました。
普段あまり意識することのない`django_content_type`にも触れられました。

一応[GitHub](https://github.com/donaisore/django-docker/tree/rename_app)です。

ありがとうございました。


## 参考
- [How to change the name of a Django app?](https://stackoverflow.com/questions/8408046/how-to-change-the-name-of-a-django-app)