在Django的两个日期之前从数据库订购数据

时间:2018-12-03 16:32:06

标签: python mysql django django-models

我在Django模型中订购两个日期时遇到问题。我有一个模型来保存文档记录,如下所示:

class Document(models.Model):
    document_title = models.CharField(max_length=100)
    document = models.FileField()
    date_of_signature = models.DateField()
    date_of_rectification = models.DateField(null=True, blank=True)

    class Meta:
        ordering = ['-date_of_signature', '-date_of_rectification']

我已经使用类Meta Options.ordering来订购日期并通过订购获得了结果,但是我的具体问题是:

  1. 如果两个字段都有日期,则应该基于两个字段进行排序。
  2. 但是date_of_rectification可以是null的值,因此,如果它是null,则必须使用最新的date_of_signature进行订购,而我在类Meta {{ 1}}

我搜索了许多关于stackoverflow的问题,发现此MySQL查询Mysql order items by the newest of 2 dates恰好解决了我在MySQL数据库中的问题,并在Django Manager.raw()上实现了此查询,如下所示,我得到了预期的结果。但这并没有帮助我在 Django Admin 上订购数据,这不是我的要求。而且我还想知道是否可以使用RawQueryset插入的Django Queryset解决此查询。

Options.ordering

2 个答案:

答案 0 :(得分:1)

您可以在两个字段中注释查询,以获取两个字段中的最大一个:

from django.db.models.functions import Greatest
Document.objects.annotate(
    latest=Greatest('-date_of_signature', '-date_of_rectification')
).order_by('-latest')

关于管理员内的顺序,您可以覆盖get_queryset方法以使用相同的注释:

from django.db.models import Count

class DocumentAdmin(admin.ModelAdmin)

    def queryset(self, *args, **kwargs):
        qs = super(DocumentAdmin, self).queryset(*args, **kwargs)
        qs = qs.annotate(
            latest=Greatest('-date_of_signature', '-date_of_rectification'))
        )
        return qs.order_by('-latest')

或者,如果上述方法不起作用(例如,空字段处理不是您想要的),则可以对这两个字段进行反规范化,从而创建第三个名为last_modified的字段,其中{ {1}}和editable=False。然后,您可以在db_index=True上进行计算。按顺序排序该字段将在查询方面更加高效,但要付出更多数据库字段和几行代码的代价。

答案 1 :(得分:0)

尝试了许多选项之后,我找到了一种解决方案,可以使用django中的conditional Expressions获得所需的结果,如下所示:

Document.objects.annotate(
    latest=Case(
        When(date_of_rectification__isnull=True, then=F('date_of_signature')),
        When(date_of_rectification__gte=F('date_of_signature'), then=F('date_of_rectification')),
        default=F('date_of_rectification')
    )
).order_by('-latest')

django将转换此代码的MySQL查询为:

SELECT myapp_document.document_title, myapp_document.document,
       myapp_document.date_of_signature, myapp_document.date_of_rectification, 
       CASE WHEN myapp_document.date_of_rectification IS NULL THEN myapp_document.date_of_signature 
       WHEN myapp_document.date_of_rectification >= (myapp_document.date_of_signature) THEN myapp_document.date_of_rectification 
       ELSE myapp_document.date_of_rectification END AS latest FROM myapp_document ORDER BY latest DESC

但是我不确定它的效率如何。希望有更好的答案。