Saya punya Model sederhana seperti ini:
class Order(models.Model):
created = model.DateTimeField(auto_now_add=True)
total = models.IntegerField() # monetary value
Dan saya ingin menampilkan perincian bulan demi bulan dari:
- Berapa banyak penjualan dalam sebulan (
COUNT
) - Nilai gabungan (
SUM
)
Saya tidak yakin apa cara terbaik untuk menyerang ini. Saya telah melihat beberapa pertanyaan ekstra-pilih yang tampak cukup menakutkan tetapi pikiran saya yang sederhana memberi tahu saya bahwa saya mungkin lebih baik hanya mengulang angka, mulai dari tahun / bulan awal yang sewenang-wenang dan menghitung hingga saya mencapai bulan saat ini, membuang yang sederhana kueri pemfilteran untuk bulan itu. Lebih banyak pekerjaan database - lebih sedikit stres pengembang!
Apa yang paling masuk akal bagi Anda? Adakah cara yang bagus untuk menarik kembali tabel data cepat? Atau apakah metode kotor saya mungkin ide terbaik?
Saya menggunakan Django 1.3. Tidak yakin apakah mereka telah menambahkan cara yang lebih baik GROUP_BY
baru - baru ini.
Jawaban:
Django 1.10 dan yang lebih baru
Django daftar dokumentasi
extra
sebagai usang segera . (Terima kasih telah menunjukkannya @seddonym, @ Lucas03). Saya membuka tiket dan inilah solusi yang diberikan jarshwah.from django.db.models.functions import TruncMonth from django.db.models import Count 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
Versi yang lebih lama
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')
Editan
sumber
>>> qs.extra({'month':td}).values('month').annotate(Sum('total')) [{'total__sum': Decimal('1234.56'), 'month': datetime.datetime(2011, 12, 1, 0, 0)}]
'{}.timestamp'.format(model._meta.db_table)
USE_TZ
pengaturan Django adalahTrue
, kedua versi tersebut tidak sama persis. Versi yang menggunakanTruncMonth
akan mengonversi stempel waktu ke zona waktu yang ditentukan olehTIME_ZONE
setelan sebelumdate_trunc_sql
dipotong , sedangkan versi yang menggunakan akan memotong stempel waktu UTC mentah di database.Hanya tambahan kecil untuk @tback jawaban: Itu tidak bekerja untuk saya dengan Django 1.10.6 dan postgres. Saya menambahkan order_by () di bagian akhir untuk memperbaikinya.
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()
sumber
TruncDate
memungkinkan Anda untuk mengelompokkan berdasarkan tanggal (hari dalam sebulan)Pendekatan lain adalah dengan menggunakan
ExtractMonth
. Saya mengalami masalah saat menggunakan TruncMonth karena hanya satu nilai tahun waktu yang dikembalikan. Misalnya, hanya bulan-bulan tahun 2009 yang dikembalikan. ExtractMonth memperbaiki masalah ini dengan sempurna dan dapat digunakan seperti di bawah ini:from django.db.models.functions import ExtractMonth Sales.objects .annotate(month=ExtractMonth('timestamp')) .values('month') .annotate(count=Count('id')) .values('month', 'count')
sumber
metrics = { 'sales_sum': Sum('total'), } queryset = Order.objects.values('created__month') .annotate(**metrics) .order_by('created__month')
Ini
queryset
adalah daftar pesanan, satu baris per bulan, menggabungkan jumlah penjualan:sales_sum
@Djogja 2.1.7
sumber
Inilah metode kotor saya. Ini kotor.
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
Mungkin ada cara yang lebih baik untuk mengulang tahun / bulan tetapi bukan itu yang saya pedulikan :)
sumber
Berikut adalah cara Anda dapat mengelompokkan data berdasarkan periode waktu yang berubah-ubah:
from django.db.models import F, Sum from django.db.models.functions import Extract, Cast period_length = 60*15 # 15 minutes # Annotate each order with a "period" qs = Order.objects.annotate( timestamp=Cast(Extract('date', 'epoch'), models.IntegerField()), period=(F('timestamp') / period_length) * period_length, ) # Group orders by period & calculate sum of totals for each period qs.values('period').annotate(total=Sum(field))
sumber
saya memiliki tabel pesanan di database saya. saya akan menghitung pesanan per bulan dalam 3 bulan terakhir
from itertools import groupby from dateutil.relativedelta import relativedelta date_range = datetime.now()-relativedelta(months=3) aggs =Orders.objects.filter(created_at=date_range)\ .extra({'date_created':"date(created_at)"}).values('date_created') for key , group in groupby(aggs): print(key,len(list(group)))
create_at adalah bidang tanggal waktu. Dengan fungsi tambahan yang dilakukan adalah mengambil tanggal dari nilai-nilai datetime. saat menggunakan datetime kita mungkin tidak mendapatkan hitungan yang benar karena objek dibuat pada waktu yang berbeda dalam satu hari.
Putaran for akan mencetak tanggal dan jumlah hitungan
sumber
Menurut bulan:
Order.objects.filter().extra({'month':"Extract(month from created)"}).values_list('month').annotate(Count('id'))
Berdasarkan Tahun:
Order.objects.filter().extra({'year':"Extract(year from created)"}).values_list('year').annotate(Count('id'))
Pada siang hari:
Order.objects.filter().extra({'day':"Extract(day from created)"}).values_list('day').annotate(Count('id'))
Jangan lupa untuk mengimpor Hitungan
from django.db.models import Count
Untuk django <1.10
sumber