Add Ordered MayToMany
|Reported by:||Owned by:||nobody|
|Severity:||Keywords:||ordered list many-to-many|
|Has patch:||no||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
I'm writing an extremely simple django app to store a database of publications for academics. Each publication has a title and an ordered list of authors. I want the academics in my group to enter their pubs and author lists using the django admin.
Here's the basic model:
class Author(models.Model): name = models.CharField(maxlength=30) class Pub(models.Model): title = models.CharField(maxlength=200) authors = models.ManyToManyField(Author)
However, this list of authors is unordered, and academics care a lot about author order.
Even though ordered lists are common in practice, they turn out to be very tricky to represent in django.You can introduce an intermediary table (http://www.djangoproject.com/documentation/models/m2m_intermediary/) but doing so is a bit too complicated and unintuitive, and I was personally unable to make it work properly with the admin site. (See below & attachment.)
This ticket proposes a new relation akin to "OrderedManyToManyField" that works just like ManyToMany, except that the order of elements can be specified and preserved. The code for the above would look something like:
class Author(models.Model): name = models.CharField(maxlength=30) class Pub(models.Model): title = models.CharField(maxlength=200) authors = models.OrderedManyToManyField(Author)
Note that the order must be maintained per publication; you can't just order by an attribute on the author model. Each author can be in multiple differently-ordered lists.
Just for the record, here's my current attempt at implementing an ordered list of items using an intermediary table:
from django.db import models class Author(models.Model): name = models.CharField(maxlength=30) def __str__(self): return self.name class Admin: pass class Pub(models.Model): title = models.CharField(maxlength=200) def __str__(self): return self.title class Admin: pass class PubAuthor(models.Model): author = models.OneToOneField(Author, core=True) author_number = models.IntegerField(core=True) pub = models.ForeignKey(Pub, edit_inline=models.TABULAR, num_in_admin=5, num_extra_on_change=5) class Admin: pass def __str__(self): return self.author.__str__() + ' ' + str(self.author_number)
Besides being more complicated to write, the admin interface for this code does not work right. I'm attaching a screenshot of viewing a pub after it has been created and saved. Note that (1) the admin user has to manually specify the index of each pub rather than have it implicit in the order of the list, and (2) for some reason the authors cannot be changed after the pub has been saved and committed to the database.
(Including links in a separate post to dodge spam filter.)