Code

Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#6118 closed (duplicate)

Add Ordered MayToMany

Reported by: toomim@… Owned by: nobody
Component: Uncategorized Version: master
Severity: Keywords: ordered list many-to-many
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

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

Attachments (1)

badorderedmanytomany.png (20.3 KB) - added by toomim@… 6 years ago.
Screenshot of failed ordered list attempt

Download all attachments as: .zip

Change History (8)

Changed 6 years ago by toomim@…

Screenshot of failed ordered list attempt

comment:2 Changed 6 years ago by anonymous

Links:

My failed approach uses an intermediary table: http://www.djangoproject.com/documentation/models/m2m_intermediary/

Rails has something called "acts_as_list": http://api.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html#M000667

There is existing code that might be reusable here:
http://www.djangosnippets.org/snippets/259/
...which I believe was derived from here:
http://www.djangosnippets.org/snippets/245/

However this code does not include an Admin interface.

comment:3 Changed 6 years ago by brosner

  • Resolution set to duplicate
  • Status changed from new to closed

Marking duplicate of #6095, which fixes the real problem.

comment:4 follow-up: Changed 6 years ago by toomim@…

  • Resolution duplicate deleted
  • Status changed from closed to reopened

Although 6095 improves the workaround of using intermediate tables, they still do not provide the functionality of ordered lists.

Intermediate tables can be hard to wrap one's head around (see the mailing list posts).

The ideal admin interface has a list of items that can be dragged and dropped to reorder:

Item 1 [x delete]
Item 2 [x delete]
[+ add item]

Not a two-column table of fixed length with integers in one column:

Item 1  |  2
Item 2  |  1
______  |  _
______  |  _
______  |  _
______  |  _

In the latter, the admin user has to manually track the integers, add rows, etc.

comment:5 in reply to: ↑ 4 Changed 6 years ago by anonymous

Replying to toomim@cs.washington.edu:

In the latter, the admin user has to manually track the integers, add rows, etc.

I should clarify: the real problem with the intermediate table UI is that the admin user has to know what in the world is going on in the first place. It makes much more sense to *present* an ordered list as an ordered list than as a two-column table. An admin view for an ordered list, even if the underlying code used intermediate tables, would be great.

comment:6 Changed 6 years ago by brosner

  • Resolution set to duplicate
  • Status changed from reopened to closed

This can already be done with the newforms-admin and will be merged in once completed. Marking a duplicate of #13 which will be visited when admin UI work starts after newforms-admin is merged in to trunk.

comment:7 Changed 6 years ago by toomim@…

Great, thanks for clarifying.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.