2

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

vjuluss
  • 31
  • 2

3 Answers3

1

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]
Hybrid
  • 6,741
  • 3
  • 25
  • 45
  • Sounds interesting, but how do I call plug_into and unplug in my admin page ? – vjuluss Apr 04 '19 at 19:54
  • @vjuluss I would override the admin `save_model()` method to call the `plug_int()` and `unplug()` methods. An example can be shown here: https://stackoverflow.com/a/21852651/3345051 – Hybrid Apr 04 '19 at 20:11
  • if self.plugged_into, call plug_into, else call unplug ? – vjuluss Apr 04 '19 at 20:20
  • @vjuluss I would check if the instance has changed, and if it did, call `obj.plug_into()` or `obj.unplug()` depending on if there is a new value for `obj.plugged_into`. You can see how to do it here https://stackoverflow.com/questions/8056179/django-admin-save-model-method-how-to-detect-if-a-field-has-changed/26001986 – Hybrid Apr 04 '19 at 20:29
  • Well I can't make it work... souldn't work on problem like this at the end of the day... – vjuluss Apr 04 '19 at 20:43
  • @vjuluss No problem! Sometimes you need to take a break to get a clear mind. I would like to help you more, but it becomes very difficult since I don't have a good testing environment. You should give it another try soon, and can always ask another question regarding overriding `save_model()` in django admin (which I know very little about). Hopefully someone can help you tackle that second part of the problem. – Hybrid Apr 04 '19 at 20:45
1

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()   
echefede
  • 497
  • 4
  • 13
  • 1
    There are three issues with this. 1) `self.plugged_into.plugged_into` is never saved, 2) There is a race condition if you *do* use `self.plugged_into.plugged_into.save()` - which will happen first, the `.save()` of the secondary instance, or the initial instance? 3) There will be a loop of infinite `save()` methods calling each other – Hybrid Apr 04 '19 at 19:52
  • The problem with this is when I want to "unplug", Django returns an error saying that None has no object "plugged_into" – vjuluss Apr 04 '19 at 19:53
0

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.

HuLu ViCa
  • 5,077
  • 10
  • 43
  • 93
  • I think I understand your suggestion. I could give it a try later when I can code. But I don't think this will show the reverse connexion in the admin interface... ? – vjuluss Apr 05 '19 at 13:12