【データあり!】Djangoでmysqlのリレーション(join)を実現するには?

  • 2020年8月6日
  • 2023年1月7日
  • mysql, Python
  • 1487view

■この記事を読むとわかること

  1. Djangoでmysqlの簡単なjoinの方法が分かる
  2. joinされた側のデータがどこにあるのか分かる

 

データの準備

説明の前に、どのようなデータを扱うのか解説します。

今回は、本と(本の)ジャンルの管理の例で解説します。

ジャンルマスターがあり、本には必ずジャンルマスターのIDを持っている状態になります。

 

扱うデータについて

■ジャンルマスター

m_genre

 

■本マスター

m_books

 

データ構成は以下の通り。

m_genreテーブル

 

m_booksテーブル

DB(データベース)にデータを流し込むためsqlを用意します。

今回は、すぐに試せるようにcreate文、insert文を用意しました。


CREATE TABLE `m_genre` (
  `id` int(11) NOT NULL,
  `name` varchar(45) DEFAULT NULL COMMENT 'ジャンル名',
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  `del` tinyint(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `` (`id`,`name`,`created`,`modified`,`del`) VALUES (1,'プログラミング','0000-00-00 00:00:00',NULL,0);
INSERT INTO `` (`id`,`name`,`created`,`modified`,`del`) VALUES (2,'家事代行',NULL,NULL,0);
INSERT INTO `` (`id`,`name`,`created`,`modified`,`del`) VALUES (3,'動物図鑑',NULL,NULL,0);



CREATE TABLE `m_books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `genre_id` int(11) DEFAULT NULL COMMENT 'ジャンルID  m_genre.idの外部参照', 
  `name` varchar(45) DEFAULT NULL COMMENT '名前',
  `count` int(10) DEFAULT '0' COMMENT '在庫',
  `created` datetime NOT NULL,
  `modified` datetime DEFAULT NULL,
  `del` tinyint(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`,`created`),
  UNIQUE KEY `uniqe` (`id`,`created`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

INSERT INTO `` (`id`,`genre_id`,`name`,`count`,`created`,`modified`,`del`) VALUES (1,1,'プログラミング1',2,'0000-00-00 00:00:00',NULL,0);
INSERT INTO `` (`id`,`genre_id`,`name`,`count`,`created`,`modified`,`del`) VALUES (2,1,'プログラミング2',10,'0000-00-00 00:00:00',NULL,0);
INSERT INTO `` (`id`,`genre_id`,`name`,`count`,`created`,`modified`,`del`) VALUES (3,2,'家事代行1',2,'0000-00-00 00:00:00',NULL,0);
INSERT INTO `` (`id`,`genre_id`,`name`,`count`,`created`,`modified`,`del`) VALUES (4,2,'家事代行2',2,'0000-00-00 00:00:00',NULL,0);
INSERT INTO `` (`id`,`genre_id`,`name`,`count`,`created`,`modified`,`del`) VALUES (5,2,'家事代行3',2,'0000-00-00 00:00:00',NULL,0);

 

実際にjoinするコードを書いてみる

データが準備できたら以下のようにコードを書きます。

 

models.py

from django.db import models

class MGenre(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=45, blank=True, null=True)
    created = models.DateTimeField(blank=True, null=True)
    modified = models.DateTimeField(blank=True, null=True)
    del_field = models.IntegerField(db_column='del')  # Field renamed because it was a Python reserved word.

    class Meta:
        managed = False
        db_table = 'm_genre'


class MBooks(models.Model):

    #DBのカラム名は「genre_id」だが、idの部分は削除すること
    #ForeignKeyを使って、MGenre側(MGenre.id)を参照する
    genre = models.ForeignKey(MGenre, on_delete=models.CASCADE)
    
    name = models.CharField(max_length=45, blank=True, null=True)
    count = models.IntegerField(blank=True, null=True)
    created = models.DateTimeField()
    modified = models.DateTimeField(blank=True, null=True)
    del_field = models.IntegerField(db_column='del')  # Field renamed because it was a Python reserved word.

    class Meta:
        managed = False
        db_table = 'm_books'
        unique_together = (('id', 'created'), ('id', 'created'),)


上記のように

bookにgenreIDに対して外部キーの設定をする。

ForeignKey」を指定することにより、genru側のデータを取れるようになります。

 

view側の実際のコードは以下の通り。

views.py

from django.shortcuts import render
from django.http import HttpResponse
from .models import MBooks, MGenre

def index(request):

	#通常のクエリ
	book_list = MBooks.objects.filter(genre_id=1).values()
	print(book_list)
	
	#https://docs.djangoproject.com/en/3.0/topics/db/queries/#one-to-many-relationships
	#本(MBooks)とジャンル(MGenre)のリレーション
	#モデル側の設定はgenre = models.ForeignKey(MGenre, on_delete=models.CASCADE)
	#この方法でMGenre側のデータを取ることができる。
	e = MBooks.objects.get(id=2)
	
	#e.genreと指定することでMGenre側のデータを取得できる
	print(e.genre.name)

	
	#1ジャンルで複数の本があるケースでデータ取得
	e = MBooks.objects.filter(genre_id=1)
	print(e[0].genre.name) #配列1番めのジャンル側のデータ取得してみる


	return HttpResponse("Hello, world. You're at the polls index.")
 

これでjoin側のデータを取ることができました(詳しくはコード内のコメントを参照してください)。

上記の方法は、実装が簡単なのですぐに反映ができる。

データが比較的少ないとき、実装速度を優先させるときに良いと思う。

しかし、sqlの最適化を考える場合は、prefetch_relatedやselect_relatedを使ったほうがよいです。

prefetch_relatedやselect_relatedについては、別の機会に紹介したいと思います!

 

最後に

最後に、ソースコードの全体が分かるように、GitHubにもコードを上げさせて頂きました。

https://github.com/jshirius/django_book_project

もし必要ならば参考にしてみてください!

 

\IT未経験者からのサポートあり!転職サービス3選!!/

サービス名
未経験 未経験OK 未経験の転職専用 経験者向け
公開の求人数 ITエンジニア全体で1万件以上
ITエンジニア未経験で600件以上
未公開 5,000件以上
利用対象 全職種 IT特化 IT特化
特徴 ✓誰もが知る転職サービス
✓経歴を登録しておくとオファーが来る
✓企業担当者と条件交渉
✓スキルの身につく企業を紹介
✓IT専門のエージェントが対応
✓転職成功すると年収200万円以上の大幅アップがある
転職サポート内容
  • 求人検索
  • 企業担当者と交渉
  • 求人紹介
  • ライフプランのサポート
  • キャリア相談
  • 求人紹介
  • 提出書類の添削
  • 面接対策
公式サイト リクナビネクスト テックゲート レバテックキャリア
最新情報をチェックしよう!
>プログラミングスクール検索・比較表サイト

プログラミングスクール検索・比較表サイト

ワンクリック、さらに詳細に条件を指定してプログラミングスクールの検索ができます。さらに比較表により特徴を細かく比較できる!

CTR IMG