【问题标题】:Order data from database by two dates in django在 django 中按两个日期从数据库中订购数据
【发布时间】:2018-12-03 16:32:06
【问题描述】:

我在 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,而我没有使用元类Options.ordering

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

Model.objects.raw("""SELECT * FROM document
                ORDER BY IF(date_of_rectification > date_of_signature,
                date_of_rectification, date_of_signature)""")

【问题讨论】:

    标签: python mysql django django-models


    【解决方案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 的字段 editable=Falsedb_index=True。然后您可以在save() 上计算它。以更多的数据库字段和一些额外的代码行为代价,对该字段进行排序将更加有效地进行查询。

    【讨论】:

    • 感谢您的回复罗伯特。但是这个查询集并没有为我提供我想要的解决方案,而是它的输出是随机的,我不知道它是如何输出的。 Document.objects.filter(date_of_rectification__gte=F('date_of_signature') 也实现了相同的输出行为。您在save() 方法中对数据进行排序的答案我认为会更方便,并且会关注它。如果您对此有解决方案,我将很高兴知道。
    【解决方案2】:

    在尝试了这么多选项后,我找到了一个解决方案,我们可以在 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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-13
      • 2019-07-18
      • 1970-01-01
      • 2012-05-01
      • 2011-11-25
      • 1970-01-01
      • 2016-08-15
      • 2013-10-19
      相关资源
      最近更新 更多