vjuluss
vjuluss

Reputation: 31

Django - Symmetrical relation OneToOneField

I'm writing and app to manage my network equipments. I created a model, RJ45port, which I can add to my equipment as needed. A RJ45port can be plugged into an other RJ45port and only one.

Here is the model I created :

class RJ45port(models.Model):
    plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True)

When I "plug" a RJ45port into another, I want the second one to have "plugged_into" set to the first one. I want the relation to be symmetrical. If I "unplug", I want both of the RJ45 ports to have "plugged_into" set to null, or blank.

I found a bit of code, it might be a hint :

def save(self, *args, **kwargs):
    super(RJ45port, self).save()    
    self.plugged_into.plugged_into = self

To be honest I'm a bit lost here and it's the final step I need to get this app functional...

Upvotes: 2

Views: 588

Answers (3)

HuLu ViCa
HuLu ViCa

Reputation: 5452

Another option is to use a related_name so you can make a reverse access from the referenced instance, so you can say that the relationship becomes "symetrical". The only downside is that you can't use the same name to reference both connections:

class RJ45port(models.Model):
    plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True, related_name='plugged_from')

In this example, plugged_from can be queried like any other field from the referenced instance.

Upvotes: 0

echefede
echefede

Reputation: 536

You are right. Simply override the save method. But call super().save() at the end:

class RJ45port(models.Model):
    plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True)

    def save(self, *args, **kwargs): 
        self.plugged_into.plugged_into = self
        super(RJ45port, self).save()   

Upvotes: 1

Hybrid
Hybrid

Reputation: 7049

You are best suited just making a model plug_into() method, and then using it to "plug" one instance into another, as well as an unplug() method.

Example:

class RJ45port(models.Model):
    plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True)

    def plug_into(self, instance):
        self.plugged_into = instance
        instance.plugged_into = self

        self.save(update_fields=['plugged_into'])
        instance.save(update_fields=['plugged_into'])
        return [self.plugged_into, instance.plugged_into]

    def unplug(self):
        self.plugged_into.plugged_into = None
        self.plugged_into = None

        self.plugged_into.save(update_fields=['plugged_into'])
        self.save(update_fields=['plugged_into'])
        return [self.plugged_into, instance.plugged_into]

And then you can call it like this:

port_1 = Port.objects.all()[0]  # First port
port_2 = Port.objects.all()[1]  # Second port
port_1.plug_into(port_2)  # Should return [instance, instance]
port_1.unplug()  # Should return [None, None]

Upvotes: 1

Related Questions