自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

【Django】逆参照のrelated_nameを使用して1側から多側のデータを取り出す【models.ForeignKey()】

thumbnail

逆参照を使うことで、1側から多側のデータを取り出すことができる。

Djangoで1対多のリレーションを構築する【カテゴリ指定、コメントの返信などに】【ForeignKey】』から引用した下記モデル。

class Category(models.Model):

    name    = models.CharField(verbose_name="カテゴリ名",max_length=20)

    def __str__(self):
        return self.name


class Topic(models.Model):

    category    = models.ForeignKey(Category,verbose_name="カテゴリ",on_delete=models.CASCADE)
    comment     = models.CharField(verbose_name="コメント",max_length=2000)

    def __str__(self):
        return self.comment

通常、Topic側(多側)から、Category(1側)を呼び出すには、

topics  = Topic.objects.all() 

for topic in topics:
    print(topic.category.name)

このようにcategoryフィールド(ForeignKey)を参照すると良い。

しかし、Category(1側)からTopic(多側)を呼び出すには、逆参照を使う

categories  = Category.objects.all() 

for category in categories:
    # カテゴリに紐付いている全てのTopicを取り出す。
    print( category.topic_set.all() )

このtopic_setという名前は、[紐付いているモデルクラスの小文字]_setという命名規則がある。そのため、TopicDataの場合、topicdata_setとなる。

また、この .all() の挙動は多対多の.all() と同様、モデルオブジェクトのリストを返す。

class Category(models.Model):

    name    = models.CharField(verbose_name="カテゴリ名",max_length=20)

    def __str__(self):
        return self.name


class Topic(models.Model):

    category    = models.ForeignKey(Category,verbose_name="カテゴリ",on_delete=models.CASCADE, related_name="topics")
    comment     = models.CharField(verbose_name="コメント",max_length=2000)

    def __str__(self):
        return self.comment

このようにrelated_nameを指定した場合、逆参照はこうなる。

categories  = Category.objects.all() 

for category in categories:
    # カテゴリに紐付いている全てのTopicを取り出す。
    print( category.topics.all() )

related_nameに指定した名前で逆参照ができる。

モデルメソッド VS 逆参照のrelated_name

時として、逆参照よりもモデルメソッドのほうが有効な場面もある。

一例を下記に記す。

テンプレートから複雑な条件を指定した逆参照がしたい場合

テンプレートから逆参照をする場合、引数を指定することはできない。

故に、並び替えや複雑な条件を指定しての逆参照がしたい場合、前もってメソッドに処理をまとめておく必要がある。

下記はその例である。

from django.db import models
from django.utils import timezone

class Topic(models.Model):

    dt          = models.DateTimeField(verbose_name="投稿日時",default=timezone.now)
    comment     = models.CharField(verbose_name="コメント",max_length=2000)

    def images(self):
        return TopicImage.objects.filter(topic=self.id).order_by("dt")   #上から順に 123456

class TopicImage(models.Model):

    dt          = models.DateTimeField(verbose_name="投稿日時",default=timezone.now)
    topic       = models.ForeignKey(Topic,verbose_name="トピック",on_delete=models.CASCADE)
    content     = models.ImageField(verbose_name="画像",upload_to="bbs/topic_image/content")

TopicとTopicImageをまとめて投稿している

この、Topicに紐付けられている画像を表示する際、フォームに指定された順(先に保存された順)で画像を並べて表示する場合、order_byを使う必要がある。

related_nameをテンプレートから呼び出す場合、引数は指定できないので、このようにモデルメソッドを使う方法が妥当であると思われる。

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ