2009-05-31 14 views
37

Próbuję utworzyć system przesyłania wiadomości, w którym nadawca i odbiorcy wiadomości mogą być podmiotami ogólnymi. Wydaje się to w porządku dla nadawcy, w którym jest tylko obiekt do odniesienia (GenericForeignKey), ale nie mogę wymyślić, jak to zrobić dla odbiorców (GenericManyToManyKey?)Ogólna zależność wielu do wielu

Poniżej znajduje się uproszczony przykład. PersonClient i CompanyClient dziedziczą atrybuty od klienta, ale mają swoje własne szczegółowe dane. Ostatnia linia to punkt skupienia. W jaki sposób umożliwić odbiorcy wiadomość będzie zbiorem CompanyClients i 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) 

Odpowiedz

50

można zaimplementować to korzystając relacje rodzajowe ręcznie tworzenia tabeli połączenie pomiędzy wiadomości i odbiorcy:

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) 

byłoby użyć powyższych modeli tak:

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

Jak widać to jest znacznie bardziej szczegółowe (skomplikowane?) rozwiązanie. Prawdopodobnie utrzymam to w prostocie i pójdę z powyższym rozwiązaniem Prariedogga.

+0

Wow. To świetne rozwiązanie. Niezbyt gadatliwy, ale w stopniu bardziej skomplikowanym niż Prairiedogg. Bardzo dziękuję –

+0

W modelu 'Klient', nie rozumiem, dlaczego' MessageRecipient' jest w 'received_messages = generic.GenericRelation ("MessageRecipient", ...) '? Czy to musi być "Wiadomość"? – user3595632

+1

@ user3595632 'received_messages' to relacja wiele do wielu między' Client' i 'Message'. Dlatego musi być na 'MessageRecipient', który jawnie modeluje tę relację, ponieważ nie ma" GenericManyToManyField ". Czy to ma sens? – elo80ka

4

Można obejść ten problem poprzez uproszczenie schematu zawierać pojedynczy Client stół z flagą, aby wskazać, jakiego typu klienta było, zamiast posiadania dwóch oddzielnych modeli.

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) 

Jeśli robisz rzeczy w ten sposób, możesz nie mieć problemów z ogólnymi kluczami obcymi. Jako dodatkową wygodę możesz również napisać niestandardowe menedżery dla modelu klienta, takie jak Client.corporate.all(), Client.person.all(), aby zwrócić wstępnie przefiltrowane zestawy zapytań zawierające tylko żądany typ klientów.

To również może nie być najlepszy sposób na rozwiązanie problemu. Po prostu wyrzucam to jako jedną potencjalną możliwość. Nie wiem, czy istnieje konwencjonalna mądrość w kwestii rozbicia dwóch podobnych modeli i użycia funkcji "save override" w celu zapewnienia integralności danych. Wygląda na to, że może to być potencjalnie problematyczne ... Pozwolę, aby społeczność nauczyła mnie tego.

+0

Thanks @Prairiedogg. Zgadzam się ze wszystkim, co powiedziałeś. Nadal jestem zainteresowany, aby sprawdzić, czy istnieje rozwiązanie wykorzystujące ogólne relacje ... –

3

Bezwzględna Najlepszym sposobem, aby go o to, aby korzystać z biblioteki o nazwie django-gm2m

pip install django-gm2m 

Następnie, jeśli mamy nasze modele

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

a użytkownik

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

Możemy zrobić

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

Słodko prawda?

Więcej informacji można znaleźć tutaj:

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

Powiązane problemy