2009-05-31 23 views
37

メッセージの送信者と受信者が一般的なエンティティになるメッセージングシステムを作成しようとしています。これは参照元オブジェクト(GenericForeignKey)のみが存在するが、受信者(GenericManyToManyKey ??)についてこれについてどうやって行くのか分からない送信者にとっては問題ないようだ。一般的な多対多の関係

以下は簡単な例です。 PersonClientとCompanyClientは、Clientから属性を継承しますが、固有の詳細を持っています。最後の行は固着点です。あなたは、メッセージの受信者ではなく、CompanyClientsとPersonClients

class Client(models.Model): 
     city = models.CharField(max_length=16) 

     class Meta: 
      abstract = True 

    class PersonClient(Client): 
     first_name = models.CharField(max_length=16) 
     last_name = models.CharField(max_length=16) 
     gender = models.CharField(max_length=1) 

    class CompanyClient(Client): 
     name = models.CharField(max_length=32) 
     tax_no = PositiveIntegerField() 

    class Message(models.Model): 
     msg_body = models.CharField(max_length=1024) 
     sender = models.ForeignKey(ContentType) 
     recipients = models.ManyToManyField(ContentType) 

答えて

50

は手動でメッセージと受信者の間の接合部のテーブルを作成することで、この一般的な関係を使用して実装できます。

from django.db import models 
from django.contrib.contenttypes import generic 
from django.contrib.contenttypes.models import ContentType 

class Client(models.Model): 
    city = models.CharField(max_length=16) 

    # These aren't required, but they'll allow you do cool stuff 
    # like "person.sent_messages.all()" to get all messages sent 
    # by that person, and "person.received_messages.all()" to 
    # get all messages sent to that person. 
    # Well...sort of, since "received_messages.all()" will return 
    # a queryset of "MessageRecipient" instances. 
    sent_messages = generic.GenericRelation('Message', 
     content_type_field='sender_content_type', 
     object_id_field='sender_id' 
    ) 
    received_messages = generic.GenericRelation('MessageRecipient', 
     content_type_field='recipient_content_type', 
     object_id_field='recipient_id' 
    ) 

    class Meta: 
     abstract = True 

class PersonClient(Client): 
    first_name = models.CharField(max_length=16) 
    last_name = models.CharField(max_length=16) 
    gender = models.CharField(max_length=1) 

    def __unicode__(self): 
     return u'%s %s' % (self.last_name, self.first_name) 

class CompanyClient(Client): 
    name = models.CharField(max_length=32) 
    tax_no = models.PositiveIntegerField() 

    def __unicode__(self): 
     return self.name 

class Message(models.Model): 
    sender_content_type = models.ForeignKey(ContentType) 
    sender_id = models.PositiveIntegerField() 
    sender = generic.GenericForeignKey('sender_content_type', 'sender_id') 
    msg_body = models.CharField(max_length=1024) 

    def __unicode__(self): 
     return u'%s...' % self.msg_body[:25] 

class MessageRecipient(models.Model): 
    message = models.ForeignKey(Message) 
    recipient_content_type = models.ForeignKey(ContentType) 
    recipient_id = models.PositiveIntegerField() 
    recipient = generic.GenericForeignKey('recipient_content_type', 'recipient_id') 

    def __unicode__(self): 
     return u'%s sent to %s' % (self.message, self.recipient) 

あなたがそうのような上記のモデルを使用したい:あなたが見ることができるように

>>> person1 = PersonClient.objects.create(first_name='Person', last_name='One', gender='M') 
>>> person2 = PersonClient.objects.create(first_name='Person', last_name='Two', gender='F') 
>>> company = CompanyClient.objects.create(name='FastCompany', tax_no='4220') 
>>> company_ct = ContentType.objects.get_for_model(CompanyClient) 
>>> person_ct = ContentType.objects.get_for_model(person1) # works for instances too. 

# now we create a message: 

>>> msg = Message.objects.create(sender_content_type=person_ct, sender_id=person1.pk, msg_body='Hey, did any of you move my cheese?') 

# and send it to a coupla recipients: 

>>> MessageRecipient.objects.create(message=msg, recipient_content_type=person_ct, recipient_id=person2.pk) 
>>> MessageRecipient.objects.create(message=msg, recipient_content_type=company_ct, recipient_id=company.pk) 
>>> MessageRecipient.objects.count() 
2 

をこれははるかに冗長な(?)ソリューションです。私はおそらくそれをシンプルにし、上記のPrariedoggのソリューションに行くだろう。

+0

うわー。それは素晴らしい解決策です。あまり冗長ではありませんが、Prairiedoggよりも複雑です。ありがとうございます。 –

+0

'Client'モデルでは、なぜ' MessageRecipient'が 'received_messages = generic 'にあるのか分かりません。GenericRelation( 'MessageRecipient'、...) '?それは 'メッセージ'でなければならないのですか? – user3595632

+1

@ user3595632 'received_messages'は' Client'と 'Message'の多対多の関係です。そのため、 'GenericManyToManyField'がないので、明示的にその関係をモデル化する' MessageRecipient'になければなりません。それは理にかなっていますか? – elo80ka

4

あなたはそれがあったクライアントの種類を示すフラグを持つ単一Clientのテーブルを含めるようにスキーマを簡素化することによってこの問題を回避する可能性がある一連のことを可能にするにはどうすればよいです2つの別々のモデルを持つこと。

from django.db import models 
from django.utils.translation import ugettext_lazy as _ 

class Client(models.Model): 
    PERSON, CORPORATION = range(2) 
    CLIENT_TYPES = (
        (PERSON, _('Person')), 
        (CORPORATION, _('Corporation')), 
        ) 
    type = models.PositiveIntegerField(choices=CLIENT_TYPES, default=PERSON) 
    city = models.CharField(max_length=16) 
    first_name = models.CharField(max_length=16, blank=True, null=True) 
    last_name = models.CharField(max_length=16, blank=True, null=True) 
    corporate_name = models.CharField(max_length=16, blank=True, null=True) 
    tax_no = models.PositiveIntegerField(blank=True, null=True) 

    def save(self, *args, **kwargs): 
     """ 
     Does some validation ensuring that the person specific fields are 
     filled in when self.type == self.PERSON, and corporation specific 
     fields are filled in when self.type == self.CORPORATION ... 

     """ 
     # conditional save logic goes here 
     super(Client, self).save(*args, **kwargs) 

このようにすると、汎用外部キーをまったく使いこなす必要がなくなる可能性があります。また、クライアントモデルのカスタムマネージャー(Client.corporate.all()Client.person.all()など)を書き込んで、必要なクライアントタイプのみを含む事前フィルター処理されたクエリーセットを返すこともできます。

これは、問題を解決する最善の方法ではない場合もあります。私はちょうど1つの潜在的可能性としてそこにそれを投げている。 2つの同様のモデルを一緒に壊し、データの完全性を保証するために保存オーバーライドを使用することについての一般的な知恵があるかどうかはわかりません。それは潜在的に問題になる可能性があるようです...私はコミュニティが私にこのことを教えさせるでしょう。

+0

おかげ@Prairiedoggを。あなたが言ったすべてに同意してください。まだ一般的な関係を使って解決策があるかどうかを調べることに興味があります... –

3

これについて行くには絶対的な最善の方法は、我々のモデル

>>> from django.db import models 
>>> 
>>> class Video(models.Model): 
>>>  class Meta: 
>>>   abstract = True 
>>> 
>>> class Movie(Video): 
>>>  pass 
>>> 
>>> class Documentary(Video): 
>>>  pass 

とユーザーを持っている場合は、ライブラリーと呼ばれるジャンゴ - gm2m

pip install django-gm2m 

を使用することです

>>> from gm2m import GM2MField 
>>> 
>>> class User(models.Model): 
>>>  preferred_videos = GM2MField() 

できます

>>> user = User.objects.create() 
>>> movie = Movie.objects.create() 
>>> documentary = Documentary.objects.create() 
>>> 
>>> user.preferred_videos.add(movie) 
>>> user.preferred_videos.add(documentary) 

甘い権利?

詳細情報についてはここに行く:

http://django-gm2m.readthedocs.org/en/stable/quick_start.html