通用的多对多关系
我正在尝试创build一个邮件系统,邮件的发件人和收件人可以是通用实体。 这似乎罚款的发件人,只有对象引用(GenericForeignKey),但我不知道如何去接收者(GenericManyToManyKey ??)
下面是一个简单的例子。 PersonClient和CompanyClient从客户端inheritance属性,但有自己的具体细节。 最后一行是棘手的问题。 如何让消息收件人成为一组公司客户端和个人客户端
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)
您可以通过手动创build消息和收件人之间的联结表来实现这一点。
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
正如你所看到的,这是一个更复杂的(复杂的)解决scheme。 我可能会保持简单,并与上面的Prariedogg的解决scheme。
您可以通过简化您的模式来解决此问题,使其包含带有标志的单个Client
表,以指示它是哪种types的客户端,而不是使用两个单独的模型。
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()
等客户端模型编写自定义pipe理器,以返回仅包含所需客户端types的预过滤查询集。
这也可能不是解决你的问题的最好方法。 我只是把它扔到那里作为一个潜在的可能性。 我不知道是否有传统的智慧将两个相似的模型粉碎在一起,并使用保存覆盖来保证数据的完整性。 似乎这可能是潜在的问题…我会让社区了解我这个。
最好的方法是使用名为django-gm2m的库
pip install django-gm2m
那么如果我们有我们的模型
>>> from django.db import models >>> >>> class Video(models.Model): >>> class Meta: >>> abstract = True >>> >>> class Movie(Video): >>> pass >>> >>> class Documentary(Video): >>> pass
和一个用户
>>> 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