BETA

Djangoで継承元Modelにリレーションを貼る

投稿日:2019-11-28
最終更新:2019-11-28

概要

Djangoで継承したModelにおいて、継承元Modelにリレーションを貼りレコードを作成する方法
継承元Modelにリレーションを貼る際に、継承元Modelが意図しない値に変更される場合があるので
注意・回避方法含め書きます!

動作を確認した環境

Python==3.7.4
Django=2.2.3

結論

Model継承時に暗黙的に定義される {モデル名}_ptr フィールドにModelオブジェクトを与えます

このとき、 Model.save()Model.objects.create() を行うと継承元のModelが意図しない値に変更される場合があるため .save_base(raw=True) を使用します

# モデルの定義  
from django.db import models  


class Parent(models.Model):  
    parent_column = models.CharField(max_length=255)  

# ParentModelを継承したModel  
class Child(Parent):  
    child_column = models.CharField(max_length=255)  



parent = Parent.objects.create(  
    parent_column="Text"  
)  
# 作成済みの継承元Modelにリレーションが貼られた継承先Modelを作成する  
child = Child(  
    parent_ptr=parent,  
    child_column="Text"  
)  
child.save_base(raw=True)  

Modelを継承するとどうなるか

Modelを継承した場合は、継承元Modelのフィールドも継承されます

データベースでは、Childテーブルにはparent_columnは存在せずParentテーブルへの外部キーが作られます

class Child(Parent):  
    child_colum = models.CharField(max_length=255)  

    # 継承されたparent_columnも存在している  
    # parent_colum = models.CharField(max_length=255)  

    # 暗黙的に継承元のOneToOneFieldも定義される  
    # parent_ptr = models.OneToOneField(  
    #     Parent,  
    #     parent_link=True  
    # )  

[注意]継承元のModelに影響を与えないために

注意として

以下のように処理を行った場合、Parent.parent_columnの値は空になります

parent = Parent.objects.create(parent_column="Text")  
Child.objects.create(  
    parent_ptr=parent,  
    child_column="Text"  
)  

# ""が返る  
print(Parent.objects.get(pk=parent.pk).parent_column)  

これは、
ChildModelの parent_ptr フィールドに parent_link=Trueが定義されているために発生します

これにより、create時に parent_ptr=parent のparentにupdateを行っています
ChildModelは継承したparent_columnフィールドも定義されているため

このフィールドに値を指定せずにcreateしたことで、parent側でアップデートされることになります
これを回避するために、

Model.save_base(raw=True) を使用することで継承元Modelをupdateせずに継承先Modelをinsertすることができます

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@ibnr2hcの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう