views:

374

answers:

2

I have a general question regarding the ordering in the ManyRelatedManager object. For example, There are two classes in my model: one is called Book for books and another is Author for authors.

A many-to-many field is defined in class Book authors = models.ManyToManyField(Author)

In database, the many-to-many relationship is described in an additional table book_author with three columns: (id, book_id, author_id)

Now for a Book object b, both b.authors.all() and b.authors.iterator() are sorted in the order of book_author.author_id. But what I want is to retrieve the authors of b in the order of book_author.id because this is the right order of the authors.

Any solutions? Many thanks in advance!

A: 

The "authors" attribute of a Book instance is a manager, so manager method order_by() will work on it:

book = Book.objects.get(id=1)
authors = book.authors.order_by('-last_name')

(assuming you have a "last_name" field on Author)

dcrosta
I believe you can also define a default ordering on Authors, which should be in effect when you query Authors through an of the managers. See http://docs.djangoproject.com/en/dev/ref/models/options/#ordering
dcrosta
Note that the ordering I want does NOT depend on the attributes of authors. Actually for a same list of authors, they may have different ordering depending on what the book is. Say, if Author A and Author B have co-authored two books. In one book, A is the first book while in another book, B is the first author. Then what I want is that for book 1, the order of authors is A and B, while for book 2, the order of authors is B and A.
Bing Jian
A: 

If you want different Author ordering based on book, I would suggest using a ManyToMany field and specifying the through attribute as described here: http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships

If your through model defines an ordering column, you could query the through model directly, e.g.

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField('Author', through='BookAuthor')

class Author(models.Model):
    name = models.CharField(max_length=100)

class BookAuthor(models.Model):
    book = models.ForeignKey(Book)
    author = models.ForeignKey(Author)
    ordering = models.IntegerField()

You can now query the through model directly if you'd like:

b = Book.objects.get(pk=1)
BookAuthor.objects.filter(book=b).order_by('ordering')

You can also add the Meta ordering to add the order_by whenever querying this model...

class BookAuthor(models.Model):
    # ...
    ordering = models.IntegerField()

    class Meta:
        ordering = ('ordering',)

Now you can remove extra .order_by clause, as Django will add this itself.

b = Book.objects.get(pk=1)
BookAuthor.objects.filter(book=b)

Note: Even with a through model and a Meta ordering, Django will not add the .order_by when querying one side or the other, e.g. the following will not have the ORDER BY SQL added:

b = Book.objects.get(pk=1)
b.authors.all() # Not ordered
robhudson