【问题标题】:Django sorting by date(day)Django按日期排序(天)
【发布时间】:2016-08-01 18:11:55
【问题描述】:

我想先按天排序模型,然后按分数排序,这意味着我想查看每天得分最高的文章。

class Article(models.Model):

date_modified = models.DateTimeField(blank=True, null=True)
score = models.DecimalField(max_digits=5, decimal_places=3, blank=True, null=True)

这个答案Django Datetime Field Query - Order by Time/Hour 建议我将'__day' 与我的date_modified 一起使用:

Article.objects.filter().order_by('-date_modified__day', '-score')
FieldError: Cannot resolve keyword 'day' into field. Join on 'date_modified' not permitted.

但是我得到了与帖子中相同的错误,所以我什至不确定它应该以这种方式工作。

我使用.extra 找到了其他答案django order by date in datetime / extract date from datetime

Article.objects.filter().extra(select={"day_mod": "strftime('%d', date_modified)"}, order_by=['-day_mod', '-score'])

这适用于没有其他条件的过滤,但如果我在过滤器上应用条件,例如类别:

Article.objects.filter(category = 'Art').extra(select={'day_mod': "strftime('%d', date_modified)"}, order_by=['-day_mod', '-score'])

我收到此错误:

File "/home/mykolas/anaconda2/lib/python2.7/site-packages/IPython/core/formatters.py", line 699, in __call__
printer.pretty(obj)

File "/home/mykolas/anaconda2/lib/python2.7/site-packages/IPython/lib/pretty.py", line 383, in pretty
return _default_pprint(obj, self, cycle)

File "/home/mykolas/anaconda2/lib/python2.7/site-packages/IPython/lib/pretty.py", line 503, in _default_pprint
_repr_pprint(obj, p, cycle)

File "/home/mykolas/anaconda2/lib/python2.7/site-packages/IPython/lib/pretty.py", line 694, in _repr_pprint
output = repr(obj)

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/models/query.py", line 234, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/models/query.py", line 258, in __iter__
self._fetch_all()

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/models/query.py", line 1074, in _fetch_all
self._result_cache = list(self.iterator())

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/models/query.py", line 52, in __iter__
results = compiler.execute_sql()

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 848, in execute_sql
cursor.execute(sql, params)

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/backends/utils.py", line 83, in execute
sql = self.db.ops.last_executed_query(self.cursor, sql, params)

File "/home/mykolas/lenv/lib/python2.7/site-packages/django/db/backends/sqlite3/operations.py", line 146, in last_executed_query
return sql % params

TypeError: %d format: a number is required, not unicode

真的不知道这里发生了什么,不胜感激。

【问题讨论】:

    标签: python django sorting


    【解决方案1】:
    from django.db.models import DateTimeField
    from django.db.models.functions import Trunc
    
    Article.objects.order_by(
        Trunc('date_modified', 'date', output_field=DateTimeField()).desc(),
        '-score')
    

    【讨论】:

    • 感谢按预期工作。 django 1.10 的完美时机:)
    • 对我来说,这不适用于DateTimeField(),但它适用于DateTime()
    【解决方案2】:

    如果您使用的是 Django >= 1.8,则可以使用 Func 表达式。您遇到的问题是 % 表示法直接传递给数据库适配器,它试图用相关参数替换 %

    你可以这样使用它:

    from django.db.models import F, Func, Value
    
    Article.objects.annotate(
        day_mod=Func(Value('%d'), F('date_modified'), 
        function='strftime'
    ).order_by('-day_mod', '-score')
    

    这应该(理论上,我还没有测试过)以这样的 SQL 查询结束:

    SELECT 
      ...
      strftime('%d', "article"."date_modified") AS "day_mod"
    FROM "article"
      ...
    ORDER BY "day_mod" DESC, "score" DESC
    

    但是,我怀疑您需要将年份和月份添加到 strftime,否则您最终会发现月初的文章被月末发生的旧文章所掩盖前几个月。

    还需要注意的是,strftime 函数不受 MySQL 或 PostgreSQL 支持。据我所知,它仅受 SQLite 支持,不应在生产环境中使用。

    不幸的是,SQL 中似乎没有日期时间格式的标准。 MySQL 似乎使用DATE_FORMAT(date, format) 而PostgreSQL 使用to_char(date, format)

    如果您想同时支持 SQLite 和另一个 DB,您可以 write a custom expression 使用相关的 as_sqliteas_<db> 方法来格式化您的日期时间,但这可能有点困难,因为并非所有 DB 都使用相同的格式字符串。

    您最好的选择可能是将日期时间转换为日期,即CAST (date_modified AS DATE)。这应该适用于大多数 SQL 风格。我能想到的最简单的方法是:

    from django.db.models import DateField, Expression, F
    
    class CastToDate(Expression):
        template = 'CAST( %(expressions)s AS DATE )'
    
        def __init__(self, expressions, output_field=None, **extra):
            output_field = output_field or DateField()
            super(CastToDate, self).__init__(self, expressions, output_field, **extra)
            if len(expressions) != 1:
                raise ValueError('expressions must have exactly 1 element')
    
    Articles.objects.annotate(
        day_mod=CastToDate(F('date_modified'))).order_by('-day_mod', '-score')
    

    【讨论】:

    • 感谢 Villiers 的回答,我选择了 Vladimir's,因为它更简洁。希望不在 Django 1.10 上的人也能看到你的答案。
    【解决方案3】:

    我猜你应该使用标准日期排序,没有额外的方法。格式处理是模板的职责。

    Article.objects.filter(category='Art').order_by=('-date_modified', '-score')
    

    然后在您的模板中,您可以以您想要的格式显示日期。我给你举个例子,请参阅more options 的 api 文档。

    {{ date_modified|date:"M/d"|lower }}
    

    另一个例子(也许更适合你的需要):

    {{ date_modified|date:"D d M Y" }} {{ date_modified|time:"H:i" }}
    

    【讨论】:

    • 这并不能完全解决问题,您提出的解决方案会将最新修改的文​​章放在顶部,无论分数如何。 Xlrv 想要的是按天(不考虑时间)排序,然后按分数排序。
    猜你喜欢
    • 1970-01-01
    • 2023-03-27
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-05
    • 1970-01-01
    • 2022-11-13
    相关资源
    最近更新 更多