如何在Django中排序反向外键?

4 投票
2 回答
2739 浏览
提问于 2025-04-18 00:33

我们正在使用Django 1.4。我们有一个类叫做 A,它有一个相关的类 History。每个 A 的对象可以有很多个 History 对象。

这个 History 类里有指向其他类的 外键,比如 BusinessUnit

我想根据每个 A 对象的最新历史记录来排序,如果有的话(根据 History.logged_at 来排序)。

我尝试用 order_by('history__business_unit') 来排序,但效果不太好。请问这样排序是否可以通过数据库实现,还是必须用Python来排序?如果可以的话,我更希望通过数据库来排序。

2 个回答

7

你需要标注出最大的相关日期,筛选出历史对象中日期等于最大日期的那些,并按照一个 business_unit 属性进行排序。

from django.db.models import Max, F

a = A.objects.annotate(last_date=Max('history_set__logged_at'))\
    .filter(history_set__logged_at=F('last_date'))\
    .order_by('history_set__business_unit__<attribute>')

这样,对于每个 A 对象,你就能筛选出最后一个相关的 History 对象,并根据关联的 business_unit 进行排序。仅仅使用 history_set__business_unit 会按照业务单元的ID进行排序,所以如果你想要有意义的排序,就需要决定要根据 business_unit 的哪个属性来排序。

-4

最后我用Python进行了排序,使用了list.sort这个方法:

def human_key(key):
    parts = re.split('(\d*\.\d+|\d+)', key.lower())
    return tuple((e.swapcase() if i % 2 == 0 else float(e)) for i, e in enumerate(parts))

if (ordered in ["unregistered_business_unit", "unregistered_operational_unit", "unregistered_date"]):
    a_list = list(a_list)
    for a in a_list:
        a_history = list(a.history.all().order_by('-logged_at'))
        if (len(a_history) > 0):
            a.last_a_history = a_history[0]
        else:
            a.last_a_history = None
    if (ordered == "unregistered_business_unit"):
        a_list.sort(key=lambda a: (a.last_a_history.business_unit.description.lower() if ((a.last_a_history) and (a.last_a_history.business_unit)) else None, a.last_a_history.business_unit.identifier if ((a.last_a_history) and (a.last_a_history.business_unit)) else None, human_key(a.last_a_history.operational_unit.identifier) if ((a.last_a_history) and (a.last_a_history.operational_unit)) else None, a.mac_address), reverse=reverse_flag)
    elif (ordered == "unregistered_operational_unit"):
        a_list.sort(key=lambda a: (human_key(a.last_a_history.operational_unit.identifier) if ((a.last_a_history) and (a.last_a_history.operational_unit)) else None, a.last_a_history.business_unit.description.lower() if ((a.last_a_history) and (a.last_a_history.business_unit)) else None, a.last_a_history.business_unit.identifier if ((a.last_a_history) and (a.last_a_history.business_unit)) else None, a.mac_address), reverse=reverse_flag)
    elif (ordered == "unregistered_date"):
        a_list.sort(key=lambda a: (a.last_a_history.logged_at if (a.last_a_history) else pytz.utc.localize(datetime(MINYEAR, 1, 1)), a.mac_address), reverse=reverse_flag)

撰写回答