Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to automatically create symmetric object on Django's ManyToManyField?

I want to create a symmetric relationship on my model and also add a field in the relationship. I came across this blog (and also this another blog) and followed the steps in creating my own models.

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

But when I create the relationship from admin, only one is created. Can you please help me figure out the problem?

like image 479
Noel Pure Avatar asked Aug 03 '12 03:08

Noel Pure


1 Answers

You say you've got one half of this in the Django admin (you don't say how, but I'll assume it works unless you post the code): the remaining step is to set up the half of the relationship that's missing.

I suspect that if you put a breakpoint in your add_balancetransfer() method (import pdb; pdb.set_trace()), you'll find that it is never being called — the part of the relation that is created is done through built-in Django admin behaviour.

In short, you might just need to call add_balancetransfer() yourself when the model is saved — or, better yet, a different version that only adds the missing relationship.

One way to do this is overriding the model's save() method, but this can be a little fragile. For your situation, it may work better to fill in the missing relationship in the 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
    ...
like image 113
supervacuo Avatar answered Oct 04 '22 01:10

supervacuo