Django python rest frameworkで外部キーを使ったrelational databaseに対応する
いままでは、flowers_flowerテーブル1つしかありませんでしたが、複数のテーブルを作って、
1対多に対応したrelationalなdatabaseに対応したいと思います。
django rest frame workの公式チュートリアルを参考にしています。
relationalなdbに対応するために新しいappを作る
manage.pyがあるrestful01フォルダ内で、以下のコマンドを叩いて、
newFlowers appを作ります。
python manage.py startapp newFlowers
appを作ったら、resutful01フォルダ内のsettings.pyを変更して、
newFlowers appが認識されるようにします。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# django rest framework
'rest_framework',
'newFlowers.apps.NewflowersConfig',
モデルの作成
1対多に対応したモデルの作成を行います。
newFlowersのmodels.pyにモデルクラスを追加します。
FlowerCategoryクラス
名前のみを持つテーブルです。
name | 花のカテゴリ名 |
codeは以下になります。
class FlowerCategory(models.Model):
name = models.CharField(max_length=250)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
Flowerクラス
外部キーとして、FlowerCategoryを持たせます。
他にも以下のカラムを定義します。
name | 花の名 |
flower_category | FlowerCategoryと結びつける外部キー |
production_date | 生産日 |
has_it_competed | 競技会にでたことがあるか |
inserted_timestamp | tableに挿入された日時 |
Flowerモデルクラスのコードを書きます。
カラムを外部キーにするには、modelクラスのForeignKeyを使い以下のように書きます。
flower_category = models.ForeignKey(
FlowerCategory,
related_name='flowers',
on_delete=models.CASCADE)
related_nameにはFlowerCategoryと紐づけるづけるための名前を定義します。
stackoverflowに詳しい解説あります。
on_deleteにmodels.CASCADEを定義しています。
これは、このFlowerCategoryを消した時に、これに属するFlowerも一緒に消す設定になります。
CASCADEは数珠つなぎとかの意味ですね。
Farmerクラス
花の生産者を示すFarmerクラスを作成します。
カラムは以下のように定義します。
name | 生産者名 |
gender | 性別 |
competitions_count | 競技会にでた回数 |
inserted_timestamp | tableに挿入された日時 |
codeで着目する点は、
genderを入力する際に、choicesを使って'M','F'を簡略的に受け取り、
Male,Femaleを出力できるようにしている点です。
MALE = 'M'
FEMALE = 'F'
GENDER_CHOICES = (
(MALE, 'Male'),
(FEMALE, 'Female'),
)
gender = models.CharField(
max_length=2,
choices=GENDER_CHOICES,
default=MALE,
)
Competitionクラス
花の品評会を示すCompetitionクラスを定義します。
外部キーとして品評会に参加したFarmerクラスとFlowerクラスを持ちます
全体のカラムを以下のように定義します。
farmer | Farmerクラスへの外部キー |
flower | Flowerクラスへの外部キー |
score | 品評会点数 |
score_achievement_date | スコアを出した日付 |
class Competition(models.Model):
farmer = models.ForeignKey(
Farmer,
related_name='competitions',
on_delete=models.CASCADE)
flower = models.ForeignKey(
Flower,
on_delete=models.CASCADE)
score = models.IntegerField()
score_achievement_date = models.DateTimeField()
class Meta:
# Order by score in descending order
ordering = ('-score',)
__str__ method
Farmer,Flower,FlowerCategory,クラスに__str__ methodを継承してnameカラムを出力するようにしています。
djangoではこういう習わしらしいです。
詳しくはリンクを。。。
これらを踏まえたmodels.pyの全体のコードは以下のようになります。
class FlowerCategory(models.Model):
name = models.CharField(max_length=250)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
class Flower(models.Model):
name = models.CharField(max_length=250)
flower_category = models.ForeignKey(
FlowerCategory,
related_name='flowers',
on_delete=models.CASCADE)
production_date = models.DateTimeField()
has_it_competed = models.BooleanField(default=False)
inserted_timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
class Farmer(models.Model):
MALE = 'M'
FEMALE = 'F'
GENDER_CHOICES = (
(MALE, 'Male'),
(FEMALE, 'Female'),
)
name = models.CharField(max_length=150, blank=False, default='')
gender = models.CharField(
max_length=2,
choices=GENDER_CHOICES,
default=MALE,
)
competitions_count = models.IntegerField()
inserted_timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
class Competition(models.Model):
farmer = models.ForeignKey(
Farmer,
related_name='competitions',
on_delete=models.CASCADE)
flower = models.ForeignKey(
Flower,
on_delete=models.CASCADE)
score = models.IntegerField()
score_achievement_date = models.DateTimeField()
class Meta:
# Order by score in descending order
ordering = ('-score',)
MYSQLにdbを作ってmigrateする
前回、DatabaseをMYSQLと連動させたので、MYSQLに新しくDatabaseを作って、
migrateしてみます。
まずは、mysqlに入り以下のコマンド叩いて、new_flowers dbを作ります。
CREATE DATABASE new_flowers;
続いて、操作対象のdbをnew_flowersに変更するためにsettings.pyを以下のように修正します。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'new_flowers',
'USER': 'user',
'PASSWORD': 'password',
'HOST' : '',
'PORT': '',
}
}
続いて、
対象のルートurlがflowersと結びつけていたため、
restful01/urls.py
を修正します。
from django.conf.urls import url, include
urlpatterns = [
]
準備が終わったので、以下のように、makemigrateコマンドを叩いて、
newFlowers dbに作成したmodelのmigration fileを作りましょう。
python manage.py makemigrations newFlowers
コマンドの結果、以下のように出力されます。
Migrations for 'newFlowers':
newFlowers/migrations/0001_initial.py
- Create model Competition
- Create model Farmer
- Create model Flower
- Create model FlowerCategory
- Add field flower_category to flower
- Add field farmer to competition
- Add field flower to competition
最後にmigrateコマンドを叩いて、dbにmigrateします。
python manage.py migrate
出力は以下のようになります。
Operations to perform:
Apply all migrations: admin, auth, contenttypes, newFlowers, 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 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 newFlowers.0001_initial... OK
Applying sessions.0001_initial... OK
tableのschemaを確認する
作成したtableのschemaを確認してみましょう。
mysqlに入って以下のコマンドを叩いて、newFlowers_flower tableのschemaをチェックします。
show create table new_flowers.newFlowers_flower;
出力は以下になり、
flower_categoryが外部キーになっていることが確認できます。
| newFlowers_flower | CREATE TABLE `newFlowers_flower` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(250) NOT NULL,
`production_date` datetime(6) NOT NULL,
`has_it_competed` tinyint(1) NOT NULL,
`inserted_timestamp` datetime(6) NOT NULL,
`flower_category_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `newFlowers_flower_flower_category_id_4493dbae_fk_newFlower` (`flower_category_id`),
CONSTRAINT `newFlowers_flower_flower_category_id_4493dbae_fk_newFlower` FOREIGN KEY (`flower_category_id`) REFERENCES `newFlowers_flowercategory` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
こちらを参考に、知識をまとめています。
初版:2018/6/24