Django - wyciągnięcie danych + agregacja

0

Witam,
któryś dzień już podchodzę do tematu i nie mogę sobie dać rady.

from django.db import models
import datetime
from django.utils.timezone import utc

class Kategoria(models.Model):
    name = models.CharField(max_length=30)
    
    def __str__(self):
        return self.name

class Miesiac(models.Model):
    name = models.CharField(max_length=15)
    
    def __str__(self):
        return self.name

class Zakup(models.Model):
    category = models.ForeignKey(Kategoria, on_delete=models.CASCADE, default=1)
    month = models.ForeignKey(Miesiac, on_delete=models.CASCADE, default=datetime.datetime.now().month)
    name = models.CharField(max_length=30)
    price = models.FloatField(default=0.1)
    quantity = models.IntegerField(default=1)
    total= models.FloatField(default=0.1)
    date = models.DateField(auto_now_add=True, blank=True)

    def amount(self):
        value = (self.price * self.quantity)
        return value

    def __str__(self):
        return self.name
>>> m = Zakup.objects.filter(month__name="sierpien").values('name', 'price', 'quantity', 'total', 'category__name')
>>> m
<QuerySet [{'name': 'jeden', 'price': 2.0, 'quantity': 2, 'total': 4.0, 'category__name': 'jedzenie'}, {'name': 'dwa', 'price': 3.0, 'quantity': 3, 'total': 9.0, 'category__name': 'jedzenie'}, {'name': 'totals', 'price': 6.0, 'quantity': 10, 'total': 60.0, 'category__name': 'jedzenie'}, {'name': 'jablko', 'price': 1.5, 'quantity': 4, 'total': 6.0, 'category__name': 'jedzenie'}, {'name': 'paliwo', 'price': 100.0, 'quantity': 1, 'total': 100.0, 'category__name': 'samochod'}]>
>>> m = Zakup.objects.filter(month__name="sierpien").values('name', 'price', 'quantity', 'total', 'category__name').aggregate(Sum('total'))
>>> m
{'total__sum': 179.0}

Kiedy robię agregację, wszystko poza argumentem Sum, jest wycinane z wyniku. Chciałbym to mieć w formie:

'month__name', 'category__name', Sum('total') 

Da się to zrobić jednym zapytaniem, czy powinienem zrobić kilka?
Podpowiedzcie coś :-|

0

Nie do końca kumam co chcesz uzyskać aggregujesz po 'total' dla wszystkich kategorii
a jednoczesnie potem chesz te kategorie ?

'sierpien' |  'jedzenie, samochod' | 179.0 zł

czy:

'sierpien' |  'jedzenie' | 79.0 zł
'sierpien' |  'samochod' | 100.0 zł
0

Wersja druga mnie interesuje.

0

Używając djangowego ORMa:
Zakup.objects.values('month').annotate(expenses_sum=Sum('total')).values_list('month__name', 'category__name', 'expenses_sum')
Jak widać, rozwiązanie trochę naokoło, więc wg mnie w tym przypadku lepiej użyć raw sqla zamiast walczyć z ORMem. A najlepiej to w ogóle wywalić model Miesiac i operować tylko na DateFieldzie lub DateTimeFieldzie, z którego można łatwo wyciągnąć miesiąc, czy to w ORMie, czy SQLu.

0

Tak trochę niezwiązane z pytaniem

month = models.ForeignKey(Miesiac, on_delete=models.CASCADE, default=datetime.datetime.now().month)

Taka wartość default spowoduje, że wartość domyślna będzie zawsze taka sama dla każdego miesiąca. Mianowicie będzie taka jaka była podczas migracji. W takim przypadku musiałbyś jako default użyć funkcji żeby to działało tak jakbyś chciał.

0
iksde napisał(a):

Używając djangowego ORMa:
Zakup.objects.values('month').annotate(expenses_sum=Sum('total')).values_list('month__name', 'category__name', 'expenses_sum')
Jak widać, rozwiązanie trochę naokoło, więc wg mnie w tym przypadku lepiej użyć raw sqla zamiast walczyć z ORMem. A najlepiej to w ogóle wywalić model Miesiac i operować tylko na DateFieldzie lub DateTimeFieldzie, z którego można łatwo wyciągnąć miesiąc, czy to w ORMie, czy SQLu.

No właśnie nie do końca. Potrzebne mi są dwie daty. Data dodania do bazy oraz miesiąc, który jest wybierany w formularzu, domyślnie ustawiany na aktualny. Jednak nie jest to stała i może być taka sytuacja, że we wrześniu dodam dane, z miesiącem ustawionym na sierpień.
Dziękuję bardzo, poczytam o annotate, może zrobi mi się jaśniej w głowie.

0
anonimowy napisał(a):

Tak trochę niezwiązane z pytaniem

month = models.ForeignKey(Miesiac, on_delete=models.CASCADE, default=datetime.datetime.now().month)

Taka wartość default spowoduje, że wartość domyślna będzie zawsze taka sama dla każdego miesiąca. Mianowicie będzie taka jaka była podczas migracji. W takim przypadku musiałbyś jako default użyć funkcji żeby to działało tak jakbyś chciał.
Pozwoliło mi to ustawić bieżący miesiąc w formularzu, ale faktycznie, dopóki nie zrestartuję aplikacji, miesiąc jest ciągle ten sam, z miesiąca jej startu.
Niby fajnie, tylko teraz będę głowić, jak ustawić zakup.month = Miesiac__name w widoku formularza przed zakup.save, mając id miesiąca z datetime.datetime.now().month :)

0
anonimowy napisał(a):

Tak trochę niezwiązane z pytaniem

month = models.ForeignKey(Miesiac, on_delete=models.CASCADE, default=datetime.datetime.now().month)

Taka wartość default spowoduje, że wartość domyślna będzie zawsze taka sama dla każdego miesiąca. Mianowicie będzie taka jaka była podczas migracji. W takim przypadku musiałbyś jako default użyć funkcji żeby to działało tak jakbyś chciał.

Trochę mi z tym zeszło, ale udało się to rozwiązać to w taki sposób, w jaki chciałbym żeby to działało.

class Miesiac(models.Model):
    name = models.CharField(max_length=15)
    def __str__(self):
        return self.name

class Zakup(models.Model):
    category = models.ForeignKey(Kategoria, on_delete=models.CASCADE, default=1)
    month = models.ForeignKey(Miesiac, on_delete=models.CASCADE)
    year = models.IntegerField()
    name = models.CharField(max_length=30)
    price = models.FloatField(default=0.1)
    quantity = models.IntegerField(default=1)
    total= models.FloatField(default=0.1)
    date = models.DateField(auto_now_add=True, blank=True)

    def amount(self):
        value = (self.price * self.quantity)
        return value

    def __str__(self):
        return self.name

def zakup_nowy(request):
    if request.method == "POST":
        form = ZakupForm(request.POST)
        if form.is_valid():
            zakup = form.save(commit=False)
            zakup.total = zakup.price * zakup.quantity
            zakup.save()
            return redirect('zakup_list')
    else:
        form = ZakupForm(initial={'year': datetime.datetime.now().year, 'month': datetime.datetime.now().month })
    return render(request, 'homeb_app/zakup_edit.html', {'form': form})

0

Cel osiągnięty, jednak sam sposób w jaki wykonałem szablon, wydaje mi się straszny :-x. Jeśli ktoś mógłby rzucić okiem i coś doradzić, będę wdzięczny.
https://github.com/konradk84/homeb/blob/static_template/homeb_app/templates/homeb_app/zakup_month.html

1 użytkowników online, w tym zalogowanych: 0, gości: 1