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

このエントリーをはてなブックマークに追加