2012-01-05 52 views
45

Tôi đã có một mô hình đơn giản như thế này:Django: Nhóm theo ngày (ngày, tháng, năm)

class Order(models.Model): 
    created = model.DateTimeField(auto_now_add=True) 
    total = models.IntegerField() # monetary value 

Và tôi muốn đầu ra một sự cố tháng-by-tháng:

  • bao nhiêu bán hàng đã có trong một tháng (COUNT)
  • giá trị kết hợp (SUM)

tôi m không chắc chắn cách tốt nhất để tấn công này là gì. Tôi đã nhìn thấy một số truy vấn tìm kiếm khá đáng sợ nhưng tâm trí đơn giản của tôi đang nói với tôi rằng tôi có thể tốt hơn khi chỉ lặp lại các con số, bắt đầu từ một năm bắt đầu tùy ý/tháng và đếm cho đến khi tôi đạt đến tháng hiện tại. lọc truy vấn cho tháng đó. Công việc cơ sở dữ liệu nhiều hơn - ít căng thẳng hơn cho nhà phát triển!

Điều gì có ý nghĩa nhất đối với bạn? Có cách nào tốt đẹp để tôi có thể lấy lại một bảng dữ liệu nhanh không? Hoặc là phương pháp bẩn của tôi có lẽ là ý tưởng tốt nhất?

Tôi đang sử dụng Django 1.3. Không chắc chắn nếu họ đã thêm một cách đẹp hơn để GROUP_BY gần đây.

+0

Đây là một bản sao của http://stackoverflow.com/questions/8596856/django-get-distinct-dates-from-timestamp và http://stackoverflow.com/questions/1236865/grouping-dates -in-django/8597940 # 8597940 – tback

Trả lời

148

Django 1.10 trở lên

tài liệu Django liệt kê extra như bị phản đối sớm. (Cảm ơn bạn đã chỉ ra rằng @seddonym, @ Lucas03). Tôi đã mở một ticket và đây là giải pháp mà jarshwah cung cấp.

from django.db.models.functions import TruncMonth 
Sales.objects 
    .annotate(month=TruncMonth('timestamp')) # Truncate to month and add to select list 
    .values('month')       # Group By month 
    .annotate(c=Count('id'))     # Select the count of the grouping 
    .values('month', 'c')      # (might be redundant, haven't tested) select month and count 

phiên bản cũ

from django.db import connection 
from django.db.models import Sum, Count 
truncate_date = connection.ops.date_trunc_sql('month', 'created') 
qs = Order.objects.extra({'month':truncate_date}) 
report = qs.values('month').annotate(Sum('total'), Count('pk')).order_by('month') 

Chỉnh sửa

  • Added đếm
  • thông tin gia tăng cho django> = 1,10
+16

Ồ vâng, điều đó có ý nghĩa hơn nhiều so với hacktastrophe của tôi. Có một số điểm. – Oli

+9

+1 cho hacktastrophe – tback

+0

Tôi không cho rằng có một cách đơn giản để chuyển đổi ngày thành 'datetime.date' đúng đắn? Hoặc trong mẫu hoặc là một phần của truy vấn? Tôi hiện đang chuyển đầu ra này thông qua một danh sách-comprehension để sắp xếp ngày ra. – Oli

0

Đây là phương pháp bẩn của tôi. Nó bẩn.

import datetime, decimal 
from django.db.models import Count, Sum 
from account.models import Order 
d = [] 

# arbitrary starting dates 
year = 2011 
month = 12 

cyear = datetime.date.today().year 
cmonth = datetime.date.today().month 

while year <= cyear: 
    while (year < cyear and month <= 12) or (year == cyear and month <= cmonth): 
     sales = Order.objects.filter(created__year=year, created__month=month).aggregate(Count('total'), Sum('total')) 
     d.append({ 
      'year': year, 
      'month': month, 
      'sales': sales['total__count'] or 0, 
      'value': decimal.Decimal(sales['total__sum'] or 0), 
     }) 
     month += 1 
    month = 1 
    year += 1 

Có cũng có thể là một cách tốt hơn của Looping năm/tháng nhưng đó không phải là thực sự những gì tôi quan tâm :)

+0

BTW Nó sẽ hoạt động tốt nhưng bạn biết vòng lặp qua tháng cũng không phải là một ý tưởng tuyệt vời. Điều gì nếu ai đó muốn làm cho nó trong ngày của một tháng thì vòng lặp này sẽ lặp lại 30-31 ngày. nếu không nó hoạt động tốt –

3

Cách tiếp cận khác là sử dụng ExtractMonth. Tôi gặp rắc rối khi sử dụng TruncMonth do chỉ có một giá trị năm datetime được trả về. Ví dụ, chỉ những tháng trong năm 2009 đã được trả lại.ExtractMonth cố định vấn đề này một cách hoàn hảo và có thể được sử dụng như dưới đây:

from django.db.models.functions import ExtractMonth 
Sales.objects 
    .annotate(month=ExtractMonth('timestamp')) 
    .values('month')       
    .annotate(count=Count('id'))     
    .values('month', 'count') 
-1

By tháng:

Order.objects.filter().extra({'month':"Extract(month from created)"}).values_list('month').annotate(Count('id')) 

By Năm:

Order.objects.filter().extra({'year':"Extract(year from created)"}).values_list('year').annotate(Count('id')) 

Bằng trong ngày:

Order.objects.filter().extra({'day':"Extract(day from created)"}).values_list('day').annotate(Count('id')) 

Đừng quên để nhập khẩu Đếm

from django.db.models import * 

Đối với django < 1,10

8

Chỉ cần một bổ sung nhỏ để @tback câu trả lời: Nó không làm việc cho tôi với Django 1.10. 6 và postgres. Tôi đã thêm order_by() ở cuối để sửa nó.

from django.db.models.functions import TruncMonth 
Sales.objects 
    .annotate(month=TruncMonth('timestamp')) # Truncate to month and add to select list 
    .values('month')       # Group By month 
    .annotate(c=Count('id'))     # Select the count of the grouping 
    .order_by() 
+0

yup: https://docs.djangoproject.com/en/1.11/topics/db/aggregation/interinter-with-default-ordering-or-order-by ... không cảm thấy như thiết kế tốt nhưng chúng ' lại rất rất thông minh những người django, vì vậy nó thực sự là. – Williams

Các vấn đề liên quan