複数の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')]
とします。