[Answer]-Django: stop foreign key column on ManyToMany table from auto-ordering

1👍

It’s important to understand that in SQL there isn’t an inherent ordering to the table; the way the information is stored is opaque to you. Rather, the results of each query are ordered according to some specification that you provide at query time.

It sounds like you want the primary key of the M2M table to do double-duty as the field that defines the ordering. In most use cases that is a bad idea. What if you decide later to switch the order of A and B in group 1? What if you need to insert a new Source in between them? You can’t do it, because primary keys are not that flexible.

The usual way to do this is to provide a specific column just for ordering. Unlike the primary key field you can change this at will, allowing you to adjust the order, insert new items, etc. In Django you would do this by explicitly declaring the M2M table (using the through field) and adding an ordering column to it. Something like:

class Group(models.Model):
    source = models.ManyToManyField('Source', through='GroupSource')

class Source(models.Model):
    content = models.CharField(max_length=8)

class GroupSource(models.Model):
    # Also look into using unique_together for this model
    group = models.ForeignKey(Group)
    source = models.ForeignKey(Source)
    position = models.IntegerField()

And your code would change to:

sequences = [['C', 'A', 'B'], ['B', 'C', 'A']]

for seq in sequences:
    group = models.Group()
    group.save()
    for position, letter in enumerate(seq):
        source = models.Source.objects.get(content=letter)
        GroupSource.objects.create(group=group, source=source, position=position)

0👍

Thanks for taking the time and effort, and I probably would have gone down the route of doing much the same by adding another field to represent the ordering. But if you can safely get the same thing for free, why bother? These were individual inserts whose order of insertion is important. What puzzled me most later was some tests I have just concluded.

I managed to get the foreign keys still ordered the way I put them in by using sql-connector on a test db with the same schema relationships between the tables. There the keys in the intermediary table holding keys to each of the ManyToMany partners do not re-organise from lowest to highest. However, the exact same code unfortunately still did on the problematic database. Hence it was not a Django thing as such.

The only real difference between the functioning and non-functioning tables was the UNIQUE attribute pointing to the ManyToMany parters i.e foreign keys to Group and Source. After removing them, the problem went away.

However, to be honest, I am not sure why. Or why Django put those UNIQUE attributes there in the first place. Not sure either whether removing them will badly affect the application going forward.

Leave a comment