【问题标题】:How to properly use order_by with nulls_last on a related one_to_many field?如何在相关的 one_to_many 字段上正确使用 order_by 和 nulls_last?
【发布时间】:2020-04-28 11:17:40
【问题描述】:

我正在尝试通过相关模型的存在来过滤和排序对象,最后强制为空。

示例模型:

class ModelA(models.Model):
    name = models.CharField(max_length=255)
    ...


class ModelB(models.Model):
    model_a = models.ForeignKey(ModelA, related_name=model_b)
    date = models.DateField()
    time = models.TimeField()
    ...

现在,我需要按一些 kwargs(正常操作)过滤 ModelA,但我还需要按 最近的 ModelB 日期和时间(将来,而不是过去)升序或使用单个查询参数降序,并且 NULLS 需要放在最后,无论升序还是降序。

我已经得出结论,我需要覆盖默认的QuerySet order_by:

class ModelAQuerySet(models.QuerySet):
    def order_by(self, *field_names):
        if 'model_a_date' in field_names or '-model_a_date' in field_names:
            field_names = list(field_names)
            if 'model_a_date' in field_names:
                field_names.remove('model_a_date')
                return super().order_by(
                    'model_b__date',
                    'model_b_time',
                    *field_names
                )
            else:
                field_names.remove('-model_a_date')
                return super().order_by(
                    '-model_b__date',
                    '-model_b__time',
                    *field_names
                )
        else:
            return super().order_by(*field_names)

当我按降序排序 ('-model_a_date') 时它工作正常,因为 NULLS 在最后,但我很难将空值按升序排列在最后。我已经试过了:

return super().order_by(
    F('model_b').asc(nulls_last=True),
    'model_b__date',
    'model_b__time',
    *field_names
)

####

return super().order_by(
    F('model_b').asc(nulls_last=True)
).order_by(
    'model_b__date',
    'model_b_time',
    *field_names
)

####

return super().order_by(
    'model_b__date',
    'model_b_time',
    *field_names
).order_by(
    F('model_b').asc(nulls_last=True),
)

但这根本不起作用。当我将 order_by 与其他参数一起放入时,或者作为单独的 order_by 放在上述 order_by 之前或之后。

对此有什么想法吗?有没有人遇到过这样的问题?我在这里陷入困境,老实说,我已经没有想法了。提前致谢。

编辑

我也尝试过这样的 .union:

ModelA.objects.annotate(
    modelbs=Count('modelb')
).filter(
    modelbs__gt=0
).union(
    ModelA.objects.annotate(
        modelbs=Count('model_b')
    ).filter(
        modelbs=0
    )
)

但这会出现错误:

ORDER BY term does not match any column in the result set.

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    如果你使用链,你可以:

    • 先过滤不为空的元素,按升序或降序排列
    • 第二次选择空值
    • 最后,您将能够将您拥有的 to qs 链接起来(最后带有空值)并返回它

    这有意义吗?

    【讨论】:

    • 这听起来确实像个主意。只是大部分工作都是在 DRF 中自动完成的,所以排序发生在所有过滤之后。我想我需要覆盖更多的函数。
    猜你喜欢
    • 2016-05-15
    • 2016-10-19
    • 2017-04-11
    • 2014-06-20
    • 1970-01-01
    • 2013-05-11
    • 2011-03-10
    • 2020-06-09
    • 2012-10-09
    相关资源
    最近更新 更多