Search code examples
djangodjango-modelsforeign-keyssignals

Create another object when I create an object in django model


When i create an object like:

bus_stop_1 = 4(Foreign Key)

bus_stop_2 = 7

then it should automatically create other object with

bus_stop_1 = 7

bus_stop_2 = 4

** time and distance would be the same ** BusStop is an model My model


class ConnectedRoute(models.Model):
    bus_stop_1 = models.ForeignKey(
        BusStop, on_delete=models.CASCADE, related_name='stop1')
    bus_stop_2 = models.ForeignKey(
        BusStop, on_delete=models.CASCADE, related_name='stop2')
    distance = models.FloatField(blank=True, null=True)
    time = models.TimeField(blank=True, null=True)

I have tried this my using save method and saving the ConnectedRoute but it been called recursively again and again.

  def save(self, *args, **kwargs):
        # Check if the object is being created for the first time
        if not self.pk:
            # Create a new ConnectedRoute object with reversed bus stops
            reverse_connected_route = ConnectedRoute(
                bus_stop_1=self.bus_stop_2,
                bus_stop_2=self.bus_stop_1,
                distance=self.distance,
                time=self.time
            )
            reverse_connected_route.save()  # Save the reversed object
        super().save(*args, **kwargs)


Solution

  • You should use .get_or_create - it creates new instance only if it does not exist, like this:

    reverse_connected_route, created = ConnectedRoute.objects.get_or_create(
        bus_stop_1=self.bus_stop_2,
        bus_stop_2=self.bus_stop_1,
    )
    if (reverse_connected_route.distance, reverse_connected_route.time) != (self.distance, self.time):
        reverse_connected_route.distance = self.distance
        reverse_connected_route.time = self.time
        reverse_connected_route.save(update_fields=['distance', 'time'])
    

    This if statement will prevent recursive calls Maybe you will need to move super().save() at the top


    Another way is to use db signals, like this:
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    
    @receiver(post_save, sender=ConnectedRoute)
    def trigger_reverse_route_creation(sender, instance, **kwargs):
        if kwargs['created'] == True:
            sender.objects.get_or_create(
                bus_stop_1=instance.bus_stop_2,
                bus_stop_2=instance.bus_stop_1,
                defaults={'distance': instance.distance, 'time': instance.time}
            )
    

    But this approach won't update second route distance and time if first one was updated. You'd have to add similar if statement here to update it.