Reputation: 7903
Lets say that I have MyModel
that has created_at
and name
fields. created_at
is DateTime.
Lets say that I have the following model objects:
<id: 1, name: A, created_at: 04.06.2020T17:49>
<id: 2, name: B, created_at: 04.06.2020T18:49>
<id: 3, name: C, created_at: 05.06.2020T20:00>
<id: 4, name: D, created_at: 06.06.2020T19:20>
<id: 5, name: E, created_at: 06.06.2020T13:29>
<id: 6, name: F, created_at: 06.06.2020T12:55>
I want to make query that will return to me these models in this order:
[
04.06.2020: [<id: 1, name: A, created_at: 04.06.2020T17:49>, <id: 2, name: B, created_at: 04.06.2020T18:49>],
05.06.2020: [<id: 3, name: C, created_at: 05.06.2020T20:00>]
06.06.2020: [<id: 4, name: D, created_at: 06.06.2020T19:20>, <id: 5, name: E, created_at: 06.06.2020T13:29>, <id: 6, name: F, created_at: 06.06.2020T12:55>]
]
I want to group all models by created_at
field, but only using Date
part of DateTime
field. I know that I can make that kind of result just by using python loops, but are there any Django ORM way to solve this problem?
Upvotes: 6
Views: 1025
Reputation: 5116
The ORM itself cannot return list of models but you can use itertools.groupby
for this purpose assuming you order the dataset at the database level first.
from itertools import groupby
from operator import attrgetter
from django.db.models.functions import TruncDate
queryset = MyModel.objects.annotate(
created_at_date=TruncDate('created_at'),
).order_by('created_at')
groupedset = groupby(queryset, attrgetter('created_at_date'))
You'll then be able able to iterate over groupedset
using
for date, objects in groupedset:
...
Upvotes: 5
Reputation: 1979
I'm afraid this is currently not completely possible, at least to my knowledge (without some really deep modifications within the Django ORM).
What you can do, however, is doing date conversions and aggregations within the database:
This example assumes your database supports the
to_char
function.
qs = MyModel.objects.all()
qs.annotate(
created_at_date=models.Func(
models.F('created_at'),
models.Value("'dd.MM.yyyy'"),
function='to_char',
)
).values('created_at_date').aggregate(
# do required aggregations like Count or Sum
)
[edit] some fixes + as bryan60 correctly pointed out, you can also use Trunc
:
from django.db.models.functions import Trunc
MyModel.objects.all().annotate(
created_at_trunc=Trunc('created_at', 'day', output_field=models.DateTimeField())
).values('created_at_trunc')
Upvotes: 2
Reputation: 1058
You can try following code:
samples = MyModel.objects.all()
result = {}
for sample in samples:
date_string = sample.created_at.strftime("%m.%d.%Y")
if date_string in result:
result[date_string].append(sample)
else:
result[date_string] = [sample]
It will return output like following:
{
'06.07.2020': [<Sample: MyModel object (1)>],
'05.07.2020': [<Sample: MyModel object (2)>, <Sample: MyModel object (3)>]
}
Basically, what it does it it first fetches all the rows and then group it at python level into a dictionary.
Upvotes: 2
Reputation: 1
Not quite understanding but if you have the database created under models.py then you can create a meta class that shows ordering
Class myModel(models.Model):
#fields...
Class Meta:
ordering = ('-created_at')
Upvotes: -2