2012-08-03 4 views
6

Chcę utworzyć relację symetryczną w moim modelu, a także dodać pole w relacji. Natknąłem się na this blog (a także this another blog) i wykonałem instrukcje tworzenia własnych modeli.Jak automatycznie utworzyć obiekt symetryczny na platformie ManyToManyField Django?

class CreditCardIssuer(models.Model): 
    name = models.CharField(_('name'), max_length=256) 
    transfer_limits = models.ManyToManyField('self', through='Balancetransfer', related_name='no_transfer_allowed+', symmetrical=False, help_text=_('List of issuers to which balance transfer is not allowed.')) 

    def add_balancetransfer(self, creditcardissuer, until): 
     balance_transfer, _newly_created = Balancetransfer.objects.get_or_create(
      no_transfer_from=self, 
      no_transfer_to=creditcardissuer, 
      until=until) 
     balance_transfer, _newly_created = Balancetransfer.objects.get_or_create(
      no_transfer_from=creditcardissuer, 
      no_transfer_to=self, 
      until=until) 
     return balance_transfer 

    def remove_balancetransfer(self, creditcardissuer, until): 
     Balancetransfer.objects.filter(
      no_transfer_from=self, 
      no_transfer_to=creditcardissuer, 
      until=until).delete() 
     Balancetransfer.objects.filter(
      no_transfer_from=self, 
      no_transfer_to=creditcardissuer, 
      until=until).delete() 
     return 

    def get_transfer_limits(self, until): 
     return self.transfer_limits.filter(
      no_transfer_to__until=until, 
      no_transfer_to__no_transfer_from=self) 


class Balancetransfer(models.Model): 
    no_transfer_from = models.ForeignKey('CreditCardIssuer', related_name='no_transfer_from') 
    no_transfer_to = models.ForeignKey('CreditCardIssuer', related_name='no_transfer_to') 
    until = models.IntegerField(blank=True, null=True, help_text='Minimum card ownership period to allow balance transfer.') 

    class Meta: 
     unique_together = ('no_transfer_from', 'no_transfer_to') 

Ale kiedy tworzę związek z administratorem, tylko jeden jest tworzony. Czy możesz mi pomóc rozwiązać problem?

+0

Czy może opracować trochę bardziej szczegółowo o co ci chodzi z _tylko jeden jest created_? Lub sprecyzuj, czego się spodziewasz? –

+0

Funkcja CreditCardIssuer.add_balancetransfer pokazuje, co chcę osiągnąć, gdy utworzę związek z administratorem. W ten sposób zostanie utworzona relacja dla obu obiektów. Dziękuję za poświęcenie czasu na rozwiązanie. – noel

+0

Dwie relacje nie zostaną utworzone "automatycznie" - nie jestem jednak w 100% jednoznaczny, jeśli chodzi o oczekiwane zachowanie. Czy możesz rozwinąć? – coleifer

Odpowiedz

1

Mówisz, że masz połowę tego w administratorze Django (nie mówisz w jaki sposób, ale zakładam, że działa, dopóki nie opublikujesz kodu): pozostałym krokiem jest ustawienie połowy związek, którego brakuje.

Podejrzewam, że jeśli umieścić punkt przerwania w metodzie add_balancetransfer() (import pdb; pdb.set_trace()), przekonasz się, że to nigdy nie jest nazywany - część relacji, które jest utworzonych odbywa się poprzez wbudowany w Django administratora zachowań .

W skrócie, po prostu trzeba zadzwonić pod numer add_balancetransfer(), gdy model zostanie zapisany - lub, jeszcze lepiej, inną wersję, która dodaje tylko brakującą relację.

Jednym ze sposobów, aby to zrobić jest overriding the model's save() method, ale może to być trochę kruche. Do sytuacji, może działać lepiej, aby wypełnić brakujące relacji w model's post_save signal:

from django.db.models.signals import post_save 
from django.dispatch import receiver 

from yourapp.models import CreditCardIssuer 

@receiver(post_save, sender=CreditCardIssuer) 
def add_missing_relationship(sender, **kwargs): 
    # Add the other side of what should be a symmetrical relationship 
    ... 
+0

Dzięki za sugestie. Dodałem już pewne rejestrowanie w tych metodach i potwierdziłem, że nigdy nie są wywoływane. Jeśli nie ma automatycznego sposobu na utworzenie odwrotnego obiektu relacji, może pójdę z nadpisaniem metody save(). – noel

+0

@supervacuo wygląda jak literówka. Twój import jest dla post_save, a twój odbiorca używa pre_save. – hobs

+0

@hobs dobry połów, dziękuję. Poprawiono (do 'post_save') teraz. – supervacuo