---
slug: "django-oauth2_provider-migration-failed-foreignkey"
title: "Django のアップデート後、 oauth2_provider の マイグレーションに失敗する場合"
description: "Mac で `mkdir -p` した直後の `cd` がディレクトリ作成のタイムラグで失敗するときに `mkdir -p ... && cd $_` で確実につなぐ書き方。"
url: "https://www.ytyng.com/blog/django-oauth2_provider-migration-failed-foreignkey"
publish_date: "2022-09-16T10:11:48Z"
created: "2022-09-16T10:11:48Z"
updated: "2026-05-11T13:21:53.439Z"
categories: ["Django"]
keywords: ""
featured_image_url: "https://media.ytyng.com/resize/20230812/489b5b091ee94711a39dc15654f907c3.png.webp?width=768"
has_video: false
has_music: false
video_urls: []
music_urls: []
lang: "ja"
---

# Django のアップデート後、 oauth2_provider の マイグレーションに失敗する場合

<h1>経緯</h1>
<p>Django の django-oauth-toolkit に含まれるアプリ oauth2_provider のマイグレーションが 0004 で失敗した。</p>
<p>./manage.py migrate oauth2_provider すると、 Foreign Key の作成ができないとかで例外が出る。</p>
<p>DBのマイグレーションが途中で止まると、DDL が半端な状態になってしまうので、復旧がけっこうめんどい。</p>
<pre>SHOW CREATE TABLE oauth2_provider_application;</pre>
<p>を見て、</p>
<pre>`id` bigint(20) NOT NULL AUTO_INCREMENT,</pre>
<p>となっていれば OKで、</p>
<pre>`id` int(11) NOT NULL AUTO_INCREMENT,</pre>
<p>となっていると、<code>oauth2_provider_idtoken.application_id</code> &rarr; <code>oauth2_provider_application.id</code> のFK制約を作る所で失敗する。型が違うため。</p>
<p>原因はおそらく、昔 Djangoのバージョンが古い時、 oauth2_provider でマイグレーションを0003 まで進めている場合で、<br />oauth2_provider_application.id が int(11) で作られているため。</p>
<p>最近の oauth2_provider では、id フィールドは bigint(20) で作るようになっているため、従来の int(11)とは FK が作れなくなっている。</p>
<p>FKを作れるようにするには、 int(11) &rarr; bigint(20) に ALTER TABLE する必要がある。<br />だが、既にある id 同士で 既に FK が作られているため、int &rarr; bitint の直しは<strong>非常に面倒</strong>な作業となる。</p>
<h1>作業内容</h1>
<h2>1. マイグレーション 0004 が途中で例外で止まっていても、気にしない。0003 に戻さない。</h2>
<p>戻そうとしても面倒になるだけなので、一旦マイグレーションのことは忘れる。</p>
<h2>2. oauth2_provider の全テーブルの SHOW&nbsp;CREATE TABLE をテキストで記録しておく。</h2>
<p>だいたいこんな感じになると思う。</p>
<pre>show create table oauth2_provider_accesstoken;<br />CREATE TABLE `oauth2_provider_accesstoken` (<br /> `id` bigint(20) NOT NULL AUTO_INCREMENT,<br /> `token` varchar(255) NOT NULL,<br /> `expires` datetime(6) NOT NULL,<br /> `scope` longtext NOT NULL,<br /> `application_id` bigint(20) DEFAULT NULL,<br /> `user_id` int(11) DEFAULT NULL,<br /> `created` datetime(6) NOT NULL,<br /> `updated` datetime(6) NOT NULL,<br /> `source_refresh_token_id` bigint(20) DEFAULT NULL,<br /> `id_token_id` bigint(20) DEFAULT NULL,<br /> PRIMARY KEY (`id`),<br /> UNIQUE KEY `token` (`token`),<br /> UNIQUE KEY `source_refresh_token_id` (`source_refresh_token_id`),<br /> UNIQUE KEY `id_token_id` (`id_token_id`),<br /> KEY `oauth2_provider_accesstoken_user_id_6e4c9a65_fk_user_user_id` (`user_id`),<br /> CONSTRAINT `oauth2_provider_acce_id_token_id_85db651b_fk_oauth2_pr` FOREIGN KEY (`id_token_id`) REFERENCES `oauth2_provider_idtoken` (`id`),<br /> CONSTRAINT `oauth2_provider_acce_source_refresh_token_e66fbc72_fk_oauth2_pr` FOREIGN KEY (`source_refresh_token_id`) REFERENCES `oauth2_provider_refreshtoken` (`id`),<br /> CONSTRAINT `oauth2_provider_accesstoken_user_id_6e4c9a65_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`)<br />) ENGINE=InnoDB DEFAULT CHARSET=utf8</pre>
<pre>show create table oauth2_provider_application;<br />CREATE TABLE `oauth2_provider_application` (<br /> `id` int(11) NOT NULL AUTO_INCREMENT,<br /> `client_id` varchar(100) NOT NULL,<br /> `redirect_uris` longtext NOT NULL,<br /> `client_type` varchar(32) NOT NULL,<br /> `authorization_grant_type` varchar(32) NOT NULL,<br /> `client_secret` varchar(255) NOT NULL,<br /> `name` varchar(255) NOT NULL,<br /> `user_id` int(11) NOT NULL,<br /> `skip_authorization` tinyint(1) NOT NULL,<br /> `algorithm` varchar(5) NOT NULL,<br /> PRIMARY KEY (`id`),<br /> UNIQUE KEY `client_id` (`client_id`),<br /> KEY `oauth2_provider_application_9d667c2b` (`client_secret`),<br /> KEY `oauth2_provider_application_user_id_79829054_fk_user_user_id` (`user_id`),<br /> CONSTRAINT `oauth2_provider_application_user_id_79829054_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`)<br />) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8</pre>
<pre>show create table oauth2_provider_grant;<br />CREATE TABLE `oauth2_provider_grant` (<br /> `id` int(11) NOT NULL AUTO_INCREMENT,<br /> `code` varchar(255) NOT NULL,<br /> `expires` datetime(6) NOT NULL,<br /> `redirect_uri` longtext NOT NULL,<br /> `scope` longtext NOT NULL,<br /> `application_id` int(11) NOT NULL,<br /> `user_id` int(11) NOT NULL,<br /> `code_challenge` varchar(128) NOT NULL,<br /> `code_challenge_method` varchar(10) NOT NULL,<br /> `nonce` varchar(255) NOT NULL,<br /> `claims` longtext NOT NULL,<br /> PRIMARY KEY (`id`),<br /> KEY `oauth2_application_id_81923564_fk_oauth2_provider_application_id` (`application_id`),<br /> KEY `oauth2_provider_grant_user_id_e8f62af8_fk_user_user_id` (`user_id`),<br /> KEY `oauth2_provider_grant_c1336794` (`code`),<br /> CONSTRAINT `oauth2_application_id_81923564_fk_oauth2_provider_application_id` FOREIGN KEY (`application_id`) REFERENCES `oauth2_provider_application` (`id`),<br /> CONSTRAINT `oauth2_provider_grant_user_id_e8f62af8_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`)<br />) ENGINE=InnoDB DEFAULT CHARSET=utf8</pre>
<pre>show create table oauth2_provider_idtoken;<br />CREATE TABLE `oauth2_provider_idtoken` (<br /> `id` bigint(20) NOT NULL AUTO_INCREMENT,<br /> `jti` char(32) NOT NULL,<br /> `expires` datetime(6) NOT NULL,<br /> `scope` longtext NOT NULL,<br /> `created` datetime(6) NOT NULL,<br /> `updated` datetime(6) NOT NULL,<br /> `application_id` bigint(20) DEFAULT NULL,<br /> `user_id` int(11) DEFAULT NULL,<br /> PRIMARY KEY (`id`),<br /> UNIQUE KEY `jti` (`jti`),<br /> KEY `oauth2_provider_idtoken_user_id_dd512b59_fk_user_user_id` (`user_id`),<br /> CONSTRAINT `oauth2_provider_idtoken_user_id_dd512b59_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`)<br />) ENGINE=InnoDB DEFAULT CHARSET=utf8</pre>
<pre>show create table oauth2_provider_refreshtoken;<br />CREATE TABLE `oauth2_provider_refreshtoken` (<br /> `id` bigint(20) NOT NULL AUTO_INCREMENT,<br /> `token` varchar(255) NOT NULL,<br /> `access_token_id` bigint(20) DEFAULT NULL,<br /> `application_id` bigint(20) NOT NULL,<br /> `user_id` int(11) NOT NULL,<br /> `created` datetime(6) NOT NULL,<br /> `updated` datetime(6) NOT NULL,<br /> `revoked` datetime(6) DEFAULT NULL,<br /> PRIMARY KEY (`id`),<br /> UNIQUE KEY `access_token_id` (`access_token_id`),<br /> UNIQUE KEY `oauth2_provider_refreshtoken_token_revoked_af8a5134_uniq` (`token`,`revoked`),<br /> KEY `oauth2_provider_refreshtoken_user_id_da837fce_fk_user_user_id` (`user_id`),<br /> CONSTRAINT `oauth2_provider_refr_access_token_id_775e84e8_fk_oauth2_pr` FOREIGN KEY (`access_token_id`) REFERENCES `oauth2_provider_accesstoken` (`id`),<br /> CONSTRAINT `oauth2_provider_refreshtoken_user_id_da837fce_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`)<br />) ENGINE=InnoDB DEFAULT CHARSET=utf8</pre>
<h2>3. oauth2_provider のテーブル同士の Foreign Key Constraint をすべて消す</h2>
<pre>alter table oauth2_provider_accesstoken<br /> drop foreign key oauth2_provider_acce_id_token_id_85db651b_fk_oauth2_pr,<br /> drop foreign key oauth2_provider_acce_source_refresh_token_e66fbc72_fk_oauth2_pr;</pre>
<pre>alter table oauth2_provider_grant<br /> drop foreign key oauth2_application_id_81923564_fk_oauth2_provider_application_id;</pre>
<pre>alter table oauth2_provider_refreshtoken<br /> drop foreign key oauth2_provider_refr_access_token_id_775e84e8_fk_oauth2_pr,<br /> drop foreign key oauth2_provider_refreshtoken_user_id_da837fce_fk_user_user_id;</pre>
<p>こんな感じで消していく。<br />実際のテーブルの状態と見比べながらやってください。</p>
<h2>4. int -&gt; bigint へ変更する</h2>
<pre>alter table oauth2_provider_accesstoken<br /> modify id bigint auto_increment;</pre>
<pre>alter table oauth2_provider_application<br /> modify id bigint auto_increment;</pre>
<pre>alter table oauth2_provider_grant<br /> modify id bigint auto_increment,<br /> modify application_id bigint NOT NULL;</pre>
<p>こんな感じで。これがすべてではないかもしれない。</p>
<h2>5. FK を作り直す</h2>
<p>2 で作ったテキストのコピーを使って alter table 文を組み立てる。</p>
<pre>alter table oauth2_provider_accesstoken<br /> add CONSTRAINT `oauth2_provider_acce_id_token_id_85db651b_fk_oauth2_pr` FOREIGN KEY (`id_token_id`) REFERENCES `oauth2_provider_idtoken` (`id`),<br /> add CONSTRAINT `oauth2_provider_acce_source_refresh_token_e66fbc72_fk_oauth2_pr` FOREIGN KEY (`source_refresh_token_id`) REFERENCES `oauth2_provider_refreshtoken` (`id`);</pre>
<pre>alter table oauth2_provider_grant<br /> add CONSTRAINT `oauth2_application_id_81923564_fk_oauth2_provider_application_id` FOREIGN KEY (`application_id`) REFERENCES `oauth2_provider_application` (`id`);</pre>
<pre>alter table oauth2_provider_refreshtoken<br /> add CONSTRAINT `oauth2_provider_refr_access_token_id_775e84e8_fk_oauth2_pr` FOREIGN KEY (`access_token_id`) REFERENCES `oauth2_provider_accesstoken` (`id`),<br /> add CONSTRAINT `oauth2_provider_refreshtoken_user_id_da837fce_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`);</pre>
<h2>6. 手動でマイグレーション 0004 を実行する</h2>
<p>これが 0004 の内容。エラーが出て失敗してるなら途中までは成功している。<br />全部冪等なので、もう一度SQLを発行しても問題無い。</p>
<pre>ALTER TABLE `oauth2_provider_application` ALTER COLUMN `algorithm` DROP DEFAULT;<br />CREATE TABLE `oauth2_provider_idtoken` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `jti` char(32) NOT NULL UNIQUE, `expires` datetime(6) NOT NULL, `scope` longtext NOT NULL, `created` datetime(6) NOT NULL, `updated` datetime(6) NOT NULL, `application_id` bigint NULL, `user_id` integer NULL);<br />ALTER TABLE `oauth2_provider_accesstoken` ADD COLUMN `id_token_id` bigint NULL UNIQUE , ADD CONSTRAINT `oauth2_provider_acce_id_token_id_85db651b_fk_oauth2_pr` FOREIGN KEY (`id_token_id`) REFERENCES `oauth2_provider_idtoken`(`id`);<br />ALTER TABLE `oauth2_provider_grant` ADD COLUMN `nonce` varchar(255) DEFAULT '' NOT NULL;<br />ALTER TABLE `oauth2_provider_grant` ALTER COLUMN `nonce` DROP DEFAULT;<br />ALTER TABLE `oauth2_provider_grant` ADD COLUMN `claims` longtext NOT NULL;<br />ALTER TABLE `oauth2_provider_idtoken` ADD CONSTRAINT `oauth2_provider_idto_application_id_08c5ff4f_fk_oauth2_pr` FOREIGN KEY (`application_id`) REFERENCES `oauth2_provider_application` (`id`);<br />ALTER TABLE `oauth2_provider_idtoken` ADD CONSTRAINT `oauth2_provider_idtoken_user_id_dd512b59_fk_user_user_id` FOREIGN KEY (`user_id`) REFERENCES `user_user` (`id`);</pre>
<h2>7. フェイクマイグレーションで 0004 を終わったことにする</h2>
<pre>./manage.py showmigrations oauth2_provider<br />./manage.py migrate oauth2_provider 0004 --fake<br />./manage.py showmigrations oauth2_provider</pre>
<h2>8. 最後までマイグレーションする</h2>
<pre>./manage.py migrate oauth2_provider</pre>
<p>私の場合、</p>
<pre>django.db.utils.OperationalError: (1054, "Unknown column 'oauth2_provider_application.created' in 'field list'")</pre>
<p>が出たので、</p>
<pre>ALTER TABLE oauth2_provider_application<br />ADD COLUMN `created` datetime(6),<br />ADD COLUMN `updated` datetime(6);</pre>
<p>これを実行した。created, updated は oauth2_provider の抽象クラスのフィールドなので、 oauth2_provider のすべてのテーブルに同様のカラムを追加する必要がある。</p>
