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

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

  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

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

スポンサーリンク
PR




PR




シェアする

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

フォローする

スポンサーリンク
PR