简体   繁体   中英

Order Django queryset by two concatenated M2M fields

I have the following Django models (greatly simplified here):

class Author:
    name = models.TextField()

class Editor:
    name = model.TextField()

class Publication:
    authors = models.ManyToManyField(Author)
    editors = models.ManyToManyField(Editor)

A publication can have either authors or editors, but not both. I would like to order a the list of publications by the unification of authors and editors, alphabetically sorted. In other words, I would like to order by a virtual field, let's call it creators which is the concatenation of authors and editors.

The query:

Publication.objects.all().order_by('authors__name', 'editors__name')

... will clump together the publications that have NO authors, and within that group sort according to editors, which is NOT what I want. Effectively, it should use whatever of authors or editors is available for a publication and sort by that.

This ordering has to happen at the database level, the result sets can be huge.

One approach is creating a method called creators :

class Publication:
    authors = models.ManyToManyField(Author)
    editors = models.ManyToManyField(Editor)

    def creators(self):
        return self.authors + self.creators

But the disadvantage is that you cannot perform the sorting at the database-level but at the Python level:

sorted(Publication.objects.all(), key=lambda k: " ".join(p.name for p in k))

Can I just throw this out there?

You may want Authors and Editors to inherit from Contributors . Since both models will have stuff like first and last names, address, etc.

To sort by whichever field has a value defined, you'll need to create a combined field somewhere the QuerySet can access, either at the model level, using an custom Aggregate, or using an extra clause.

If you don't need to be totally DB agnostic extra is probably the easiest to implement:

qs = Publication.objects.extra({
   select={'contributor': "COALESCE(author.name,editor.name)"}
})
qs.extra(order_by = ['contributor'])

https://docs.djangoproject.com/en/1.6/ref/models/querysets/#django.db.models.query.QuerySet.extra

If you do need to be DB independent, you should look at using one of the implementations of concat as an aggregate. For example,

http://harkablog.com/inside-the-django-orm-aggregates.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM