2016-08-01 259 views
2

我想先按日期排序模型,然后按分数排序,这意味着我希望每天都能看到最高得分的文章。Django按日期排序(日)

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. 

但是我得到相同的错误在后,所以我甚至不能确定它应该工作的这个办法。

我发现其他答案django order by date in datetime/extract date from datetime使用.extra

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 

不太了解wha吨的发生在这里,帮助将不胜感激。

回答

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') 
+0

谢谢按预期工作。完美的时机为Django 1.10 :) – Xlrv

1

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

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

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

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

另一个例子(也许MOR适合您的需要):

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

这并不能完全解决问题,你提出的解决方案将放不管成绩如何,最新的最新修改文章。 Xlrv想要的是按日排序(不论时间),然后排序。 –

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,否则最终会在月初发布的文章被前几个月末发生的旧文章所掩埋。请注意0​​函数不支持MySQL或PostgreSQL。据我可以告诉它只有SQLite支持,不应该在生产中使用。

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

如果你想支持SQLite和另一个数据库,你可以write a custom expression有关as_sqliteas_<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') 
+0

感谢Villiers的回答,我选择了Vladimir's,因为它更简洁。希望不在Django 1.10上的人也会看到你的答案。 – Xlrv