2016-08-01 15 views
2

Ich möchte die Modelle zuerst nach Tag und dann nach Punkte sortieren, was bedeutet, dass ich die Artikel mit den höchsten Punktzahlen an jedem Tag sehen möchte.Django Sortierung nach Datum (Tag)

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) 

Diese Antwort Django Datetime Field Query - Order by Time/Hour schlägt vor, dass ich mit meiner '__day'date_modified wie in:

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

Jedoch habe ich den gleichen Fehler wie in der Post bekommen, so dass ich bin mir nicht einmal sicher, ob es das funktionieren sollte Weg.

fand ich andere Antworten django order by date in datetime/extract date from datetime.extra mit:

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

Diese zum Filtern ohne andere Bedingungen funktioniert, aber wenn ich einen Zustand auf dem Filter wie zum Beispiel eine Kategorie gelten:

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

I Erhalten Sie diesen Fehler:

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 

Ich weiß nicht wirklich wha Es geht hier weiter, Hilfe wäre willkommen.

Antwort

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

Danke funktioniert wie vorgesehen. Perfektes Timing für Django 1.10 :) – Xlrv

1

Ich denke, Sie sollten die Standardterminierung verwenden, ohne zusätzliche Methode. Die Formatverarbeitung ist eine Vorlagenverantwortlichkeit.

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

Dann in Ihrer Vorlage können Sie das Datum in dem gewünschten Format anzeigen. Ich überlasse Ihnen ein Beispiel, sehen Sie die API-Dokumentation für more options.

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

Ein weiteres Beispiel (vielleicht mor für Ihre Bedürfnisse geeignet):

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

Dies gilt ganz das Problem nicht löst, wird Ihre vorgeschlagene Lösung setzt die Zuletzt modifizierte Artikel an der Spitze, unabhängig von der Punktzahl. Was Xlrv will ist, dass es am Tag (unabhängig von der Zeit) bestellt wird und dann die Punktzahl. –

2

Wenn Sie Django verwenden> = 1,8 Sie einen Func Ausdruck verwenden können. Das Problem besteht darin, dass die Notation % direkt an den Datenbankadapter übergeben wird, der versucht, die % durch die relevanten Parameter zu ersetzen.

Sie können es wie folgt verwenden:

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

Dies sollte (theoretisch, ich habe es nicht getestet) mit einer SQL-Abfrage am Ende wie folgt:

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

Allerdings vermute ich Sie müssen das Jahr und den Monat zum strftime hinzufügen, sonst werden Sie mit den Artikeln am Anfang eines Monats enden, die von älteren Artikeln begraben werden, die am Ende der vorherigen Monate geschahen.

Es sollte auch beachtet werden, dass die strftime Funktion nicht von MySQL oder PostgreSQL unterstützt wird. Soweit ich das beurteilen kann, wird es nur von SQLite unterstützt, das in der Produktion nicht verwendet werden sollte.

Leider scheint es keinen Standard für die Datetime-Formatierung in SQL zu geben. MySQL scheint DATE_FORMAT(date, format) zu verwenden und PostgreSQL verwendet to_char(date, format).

Wenn Sie sowohl SQLite und eine andere DB unterstützen möchten, können Sie write a custom expression mit den entsprechenden as_sqlite und as_<db> Methoden, die Ihre Datetimes formatiert werden, aber es könnte ein bisschen schwierig sein, da nicht alle DBs die gleichen Formatierungszeichenfolgen verwenden.

Ihre beste Wette ist wahrscheinlich, nur die Datetime zu date, d. H. CAST (date_modified AS DATE). Dies sollte in den meisten Varianten von SQL funktionieren. Einfachste Art, wie ich mit oben kommen kann ist:

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

Danke für die Antwort Villiers, wählte ich Vladimirs als akzeptiert, da es prägnanter ist. Hoffe Leute, die nicht auf Django 1.10 sind, werden deine Antwort auch sehen. – Xlrv