2009-05-31 43 views
37

Tôi đang cố gắng tạo một hệ thống nhắn tin nơi người gửi và người nhận thư có thể là các thực thể chung chung. Điều này có vẻ tốt cho người gửi, nơi mà chỉ có đối tượng để tham chiếu (GenericForeignKey) nhưng tôi không thể tìm ra cách để làm điều này cho người nhận (GenericManyToManyKey ??)Mối quan hệ nhiều-nhiều chung chung

Dưới đây là một ví dụ đơn giản. PersonClient và CompanyClient kế thừa các thuộc tính từ Client nhưng có các chi tiết cụ thể của riêng chúng. Dòng cuối cùng là điểm gắn bó. Làm thế nào để bạn cho phép người nhận thư là một tập hợp các CompanyClients và 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) 

Trả lời

50

Bạn có thể thực hiện điều này sử dụng các mối quan hệ chung bằng cách thủ công tạo ra các bảng đường giao nhau giữa thông điệp và người nhận:

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) 

Bạn muốn sử dụng các mô hình trên như sau:

>>> 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 

Như bạn có thể nhìn thấy , đây là một giải pháp dài dòng hơn (phức tạp?). Tôi có thể giữ nó đơn giản và đi với giải pháp của Prariedogg ở trên.

+0

Chà. Đó là một giải pháp tuyệt vời. Không phải là rất tiết nhưng một mức độ phức tạp hơn Prairiedogg. Cảm ơn rất nhiều –

+0

Trong mô hình 'Client', tôi không hiểu tại sao' MessageRecipient' nằm trong 'received_messages = generic.GenericRelation ('MessageRecipient', ...) '? Nó có phải là 'Thông điệp' không? – user3595632

+1

@ user3595632 'received_messages' là mối quan hệ nhiều-nhiều giữa' Client' và 'Message'. Đó là lý do tại sao nó phải nằm trên 'MessageRecipient', mà mô hình hóa một cách rõ ràng mối quan hệ đó, vì không có' GenericManyToManyField'. Điều đó có ý nghĩa? – elo80ka

4

Bạn có thể khắc phục vấn đề này bằng cách đơn giản hóa sơ đồ của bạn để bao gồm một Client bảng duy nhất với một lá cờ để chỉ loại khách hàng đó là, thay vì có hai mô hình riêng biệt.

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) 

Nếu bạn làm mọi thứ theo cách này, bạn có thể không phải bận tâm với Khóa chung chung. Để thuận tiện hơn, bạn cũng có thể viết trình quản lý tùy chỉnh cho mô hình Khách hàng như Client.corporate.all(), Client.person.all(), để trả lại các truy vấn được lọc trước chỉ chứa loại khách hàng mà bạn muốn.

Điều này cũng có thể không phải là cách tốt nhất để giải quyết vấn đề của bạn. Tôi chỉ ném nó ra khỏi đó như một khả năng tiềm năng. Tôi không biết nếu có sự khôn ngoan thông thường về đập vỡ với nhau hai mô hình tương tự và sử dụng một ghi đè để đảm bảo tính toàn vẹn dữ liệu. Có vẻ như nó có khả năng có vấn đề ... Tôi sẽ để cho cộng đồng học tôi về điều này.

+0

Cảm ơn @Prairiedogg. Đồng ý với mọi thứ bạn nói. Tôi vẫn muốn xem liệu có giải pháp nào sử dụng các mối quan hệ chung chung hay không ... –

3

Các tuyệt đối cách tốt nhất để đi về vấn đề này là sử dụng một thư viện gọi django-gm2m

pip install django-gm2m 

Sau đó, nếu chúng ta có các mô hình của chúng tôi

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

Và một người dùng

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

Chúng tôi có thể làm

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

Ngọt phải không?

Để biết thêm thông đi ở đây:

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

Các vấn đề liên quan