Django changes order for ManyToManyField, but it have to be exactly the same as in post request

Question:

I have two models (vehicle and stop). I want to create route for every vehicle, so i’ve added manyToManyField in my vehicle model. Whenever i send post request with a few stops listed in json object django messes with the order and it won’t work like that.
What i have to do to save object with exactly the same order as it is provided in post request?

Models:

class Vehicle(models.Model):
    nr = models.CharField(max_length=24)
    route = models.ManyToManyField(Stop, related_name='stop')

class Stop(models.Model):
    name = models.CharField(max_length=64)

serializers.py:

class VehicleSerializer(DynamicDepthSerializer):

    class Meta:
        model = TrainLibrary
        fields = ['nr','route']
Asked By: Publius

||

Answers:

What you’ll need is to persist the order in the through-table/join-table in the ManyToMany relationships.

Your models will look something like this:

class Vehicle(models.Model):
    nr = models.CharField(max_length=24)
    route = models.ManyToManyField(Stop, related_name='stop', through="RouteStop")

class Stop(models.Model):
    name = models.CharField(max_length=64)

class RouteStop(models.Model):
    vehicle = models.ForeignKey("Vehicle", on_delete=models.CASCADE)
    stop = models.ForeignKey("Stop", on_delete=models.CASCADE)
    order = models.PositiveIntegerField()
    
    class Meta:
        ordering = ["order"]
        unique_together = ('vehicle', 'stop')

Now, in your serializer, you can do something like this:

class VehicleSerializer(serializers.ModelSerializer):
    route = serializers.SerializerMethodField()

    class Meta:
        model = Vehicle
        fields = ['nr', 'route']

    def get_route(self, obj):
        # you can use whatever representation you want.
        return [stop.name for stop in obj.routestop_set.all().order_by('order')]
        

Here, I’m just displaying the stop name, but you may use any other serializer to serialize the information.

Answered By: Sanyam Khurana

I found out that the best option is django-sortedm2m library. This field stores relation, exactly like default manyToManyField, but with exactly the same order that was provided in post method or create query.

from django.db import models


class childModel(models.Model):
    name = models.CharField(max_length=32)

from django.db import models
from sortedm2m.fields import SortedManyToManyField
from child.models import childModel

class myClass(models.Model):
    name = models.CharField(max_length=32)
    related_children = SortedManyToManyField(childModel)

example of post object to myClass:

{
  "name": "test",
  "related_children": [
    6,
    2,
    99,
    43,
  ] // IDs of childModel objects
}

After creating that object, we are sure that we have the same order in field "related_children" that we wanted.

This is the fastest and easiest way to achieve this, because by default django messes with order when we are creating object with linked other objects in many to many relation.

Answered By: Publius