简体   繁体   中英

Django ORM: sort by aggregate of filter of related table

Here's a subset of my model:

class Case(models.Model):
    ... # primary key is named "id"
class Employee(models.Model):
    ... # primary key is named "id"
class Report(models.Model):
    case = ForeignKey(Case, null=True)
    employee = ForeignKey(Employee)
    date = DateField()

Given a particular employee, I want to produce a list of all cases, ordered by when the employee has most recently reported on it. Those cases for which no report exists should be sorted last. Cases on the same date (including NULL) should be sorted by further criteria.

Can I express this in the Django ORM api? If so, how?

In pseudo-SQL, I think I want

Select Case.*
From Case some-kind-of-join Report
Where report.employee_id = the_given_employee_id
Group by Case.id
Order by Max(Report.date) Desc /* Report-less cases last */, Case.id /* etc. */

Do I need to introduce a many-to-many relation from Case to Employee through Report to do this in Django ORM?

Every relationship in a django model has a reverse relationship that can be easily queried (including when you are ordering) so you can do something like:

Case.objects.all().order_by('-report__date', 'another_field', 'a third field')

but this won't get you any information about a single particular employee. You could do this:

Case.objects.filter(report__employee__pk=5).order_by('-report__date', 'another_field', 'a third field')

but this won't return any Case objects that aren't edited by your particular employee.

So unfortunately, you can't natively do subqueries, so you will have to write a custom annotation query so perform the sub query (ie the last order dates for those objects last edited by a particular employee). This is untested, but it's the general idea:

Case \
    .objects \
    .all() \
    .extra(select = {
        "employee_last_edit" : """
            SELECT app_report.date
            FROM app_report
                JOIN app_case ON app_case__id = app_report.case_id
            WHERE app_report.employee_id = %d
        """ % employee.id }) \
    .order_by('-employee_last_edit' , 'something_else')

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