複数のmodelを結合する 2 - Proxy model編

複数のmodelを結合する 1ではソート対象のfield名が同一のパターンを紹介しましたが、
今回はソート対象のfield名が同一でないmodelの結合方法を紹介します。

サンプルmodel作成

登録日をBlogEntryクラスはcreatedを、Tweetクラスではpublishedとなっています。

models.py

from django.db import models

class BlogEntry(models.Model):
    title = models.CharField(max_length = 255)
    content = models.TextField()
    created = models.DateTimeField()

    def __unicode__(self):
        return self.title

class Tweet(models.Model):
    tweet_text = models.CharField(max_length = 140)
    published = models.DateTimeField()

    def __unicode__(self):
        return self.tweet_text

サンプルデータ作成

>>>from appname.models import BlogEntry, Tweet
>>>from datetime import datetime
>>>BlogEntry(title = 'Entry #1', content = 'Test', created = datetime.now()).save()
>>>Tweet(tweet_text = 'Test Tweet', published = datetime.now()).save()
>>>BlogEntry(title = 'Entry #2', content = 'Test', created = datetime.now()).save()

結合

複数のmodelを結合する 1と同じように結合してみましょう。

>>>from itertools import chain
>>>entry_list = chain(BlogEntry.objects.all(), Tweet.objects.all())
>>>entry_list = sorted(entry_list, key = lambda x: x.created)

AttributeError: 'Tweet' object has no attribute 'created'
Tweetクラスにcreatedが存在しないためエラーが発生します。

これを回避するために、proxy modelsを使ってChainTweetクラスを作成します。

models.py

class BlogEntry(models.Model):
    title = models.CharField(max_length = 255)
    content = models.TextField()
    created = models.DateTimeField()

    def __unicode__(self):
        return self.title

class Tweet(models.Model):
    tweet_text = models.CharField(max_length = 140)
    published = models.DateTimeField()

    def __unicode__(self):
        return self.tweet_text

class ChainTweet(Tweet):
    class Meta:
        proxy = True

    @property
    def created(self):
        return self.published

作成したChainTweetクラスを使用して先に進めましょう。

>>>entry_list = chain(BlogEntry.objects.all(), ChainTweet.objects.all())
>>>entry_list = sorted(entry_list, key = lambda x: x.created, reverse = True)
>>>for e in entry_list:
...    print(e, e.created.strftime("%X"))
...
(<BlogEntry: Entry #2>, '01:46:06')
(<ChainTweet: Tweet Text...>, '01:45:19')
(<BlogEntry: Entry #1>, '01:42:04')

このようにすれば、ソート対象のfield名が異なっていても結合しソートすることが出来ます。


関連してChainTweetクラスを以下のように使うことが出来ません。

>>>ChainTweet.objects.order_by('created')

FieldError: Cannot resolve keyword 'created' into field. Choices are: id, published, tweet_text となります。

createdを使いたい場合は

>>>[(e, e.created.strftime("%X")) for e in ChainTweet.objects.all()]
[(<ChainTweet: Tweet Text...>, '01:45:19')]

とします。

twitter 2010-01-15 12:52:11.247520

関連ページ

参照サイト

Recent Updates

URLConf Tip 01 - キャプチャの有無にかかわらず同一のviewで処理する
Markdownの入力補助"wmd"をAdmin siteで使う
ModelFormでfieldのwidgetを変更する
動的なformを作る 6 - Dynamic Inline Admin site編
Formsetsを使う3 - inlineformset_factory編
動的なformを作る 5 - django-dynamic-formset編
Formクラスからメディアを定義する
複数のmodelを結合する 2 - Proxy model編
複数のmodelを結合する 1
ModelFormでfieldの表示順番を変える
Admin siteのwidgetを個別に変更する
formfield_overridesを使ってAdmin siteのwidgetを変更する
Admin siteのlist_displayをカスタマイズする - リンク編
Admin siteのlist_displayをカスタマイズする - 基本編
Admin siteのTextareaの高さを自在に変更する - admin.py編