Джанго: Группировка дат по годам

Я пытаюсь создать временную шкалу из созданного поля модели, но не могу найти чистый способ сделать это. Идея заключается в том, чтобы вернуть во фронтенд (DRF) список лет с их датами внутри. Допустим, у меня есть такая модель:

class ProductModel(models.Model):
  name = models.CharField(max_length=200)
  created = models.DateField(default=datetime.date.today, blank=True)

Допустим, было создано 6 продуктов (3 из них из 2021 и 3 из 2022). Поэтому я хотел бы вернуть что-то вроде этого:

"timeline": [

    "2021": [
        ...,
        "2021-12-1",
        "2021-11-1",
        "2021-10-1",
    ],
    "2022": [
        ...,
        "2022-03-1",
        "2022-02-1",
        "2022-01-1",
    ],
]

Идея в том, чтобы возвращать только даты созданных продуктов и ничего больше. Но я не знаю, как сгруппировать даты по годам. Я пробовал с помощью annotate и values, но не получил нужного результата. Я понимаю, что в части сериализатора я должен использовать serializers.ListField(), чтобы иметь возможность передавать ему списки.

Любое предложение - я весь внимание. Заранее спасибо :)

Вы использовали key:pair внутри структуры списка, поэтому я предположу, что это неверно и вы имели в виду словарь.

Для этого вывода одно из возможных решений:

serializers.py

class TimeLineSerializer(serializers.Serializer):

    timeline = serializers.SerializerMethodField()

    def get_timeline(self, obj):
        data = {}
        pms = ProductModel.objects.all().values('created__year').distinct()
        for pm in pms:
            data[pm['created__year']] = ProductModel.objects.filter(created__year=pm['created__year']).values_list('created', flat=True)
        return data

views.py:

class TimeLineListAPIView(APIView):
    def get(self, request):
        qs = ProductModel.objects.all()
        serializer = TimeLineSerializer(qs)
        return Response(serializer.data)

вывод:

{
    "timeline": {
        "2021": [
            "2021-12-20",
            "2021-12-21",
            "2021-12-22"
        ],
        "2022": [
            "2022-12-20",
            "2022-12-21",
            "2022-12-23"
        ]
    }
}

Конечно, возможно, существует более чистое решение, о котором я не знаю.

Я не знаю, лучшее ли это решение, но я создал общую функцию get_timeline, которая работает следующим образом (спасибо Niko ниже за вдохновение):

def get_timeline(model_queryset, lookup_field):
    '''
    get timeline function will return list of dates grouping by years.
    '''
    timeline = {}
    for item in model_queryset:
        if hasattr(item, lookup_field):
            group = getattr(item, lookup_field)
            filters = { f'{ lookup_field }__year': group.year }
            timeline[group.year] = model_queryset.filter(**filters).values_list(lookup_field, flat=True)
        else:
            raise ValidationError(f'Object "{ item }" does not have "{ lookup_field }" attribute')
    return timeline

В views.py:

queryset =  ProductModel.objects.order_by('-created')
timeline = get_timeline(queryset, 'created')

И вывод:

"timeline": {
    "2021": [
        "2021-12-1",
        "2021-11-1",
        "2021-10-1",
    ],
    "2022": [
        "2022-03-1",
        "2022-02-1",
        "2022-01-1",
    ],
},

Эта функция будет вызываться в файле views.py, передавая объекты ProductModel в качестве "model_queryset" и "created" в качестве "lookup_field".

Надеемся, что это чистое решение.

Вернуться на верх