Home > Software design >  How to group a queryset by a specific field in Django?
How to group a queryset by a specific field in Django?

Time:04-28

I have a qs qs_payments that returns the following:

[
    {
        "id": 19,
        "month": "2021-05-01",
        "amount": 3745,
        "offer": 38
    },
    {
        "id": 67,
        "month": "2021-03-01",
        "amount": 4235,
        "offer": 39
    },
    {
        "id": 68,
        "month": "2021-04-01",
        "amount": 4270,
        "offer": 39
    },
    {
        "id": 69,
        "month": "2021-05-01",
        "amount": 4305,
        "offer": 39
    }
]

I now try to re-group the data so that each month contains all items of that particular month (in the given example there would be two nested items for May/2021-05-21)

I tried the following using itertools but this returns Payment object is not subscriptable. Context is that I want to render one component with its line items in React.js.

class PayrollRun(APIView):
    ...

        payment_runs = [{'subject': k, 'Results': list(g)} for k, g in itertools.groupby(qs_payments, key=itemgetter('month'))]

        print(payment_runs)

        serializer = PaymentSerializer(qs_payments, many=True)
        return Response(serializer.data)

The desired outcome is s.th. like (if that makes sense?)

[
    {
     "2021-05-01": [{
        "id": 69,
        "amount": 4305,
        "offer": 39
     },
     {
        "id": 19,
        "amount": 3745,
        "offer": 38
     }],
    },
    "2021-04-01": [{
       ...
    }],
]

Update to try to even simpler approach with a related field to Period

# models.py

class Period(models.Model):

    period = models.DateField()



class Payment(models.Model):

    offer = models.ForeignKey(Offer, on_delete=models.CASCADE)
    month = models.ForeignKey(Period, on_delete=models.CASCADE)
    amount = models.PositiveIntegerField()
# serializers.py

class PaymentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Payment
        fields = '__all__'


class PeriodSerializer(serializers.ModelSerializer):

    payments = serializers.SerializerMethodField()

    class Meta:
        model = Period
        fields = '__all__'
# views.py

    def get(self, request):
        # Loop payment table for company related payments, create dict of dict for each month
        # Get todays date
        today = datetime.date(datetime.now())

        # Get the related company according to the logged in user
        company = Company.objects.get(userprofile__user=request.user)

        # Get a queryset of payments that relate to the company and are not older than XX days
        qs_payments = Payment.objects.filter(offer__company=company)

        print(qs_payments)

        serializer = PeriodSerializer(qs_payments, many=True)
        return Response(serializer.data)

CodePudding user response:

What about something like this?

models.py

class Month(...):
   date = models.DateField(...)

Then in your serializers:

serializers.py

class PaymentSerializer(...):
    ...

class MonthSerializer(...):

    payments = serializers.SerializerMethodField()

    def get_payments(self, obj):
        payments = Payment.objects.filter(
            month = obj.date
        )
        return PaymentSerializer(payments, many=True).data

    class Meta:
        fields = ['payments', ...]
        model = Month

EDIT:

You can simplify this further by adding a foreign key relation to a Month on your Payment model, then you don't need to write the get_payments method, you can just serialize the Payments on each Month directly.

  • Related