Джанго фильтр метки времени данные GROUP BY день, неделя, месяц, год

голоса
30

У меня есть Джанго (ФПИ) приложение, в котором я хранящую данные периодических временных рядов, основанные на ответ API. Вот мой model.py

# Model to store the Alexa API Data
class Alexa(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    extra = jsonfield.JSONField(null=True)
    rank =  models.PositiveIntegerField(default=0, null=True)

Я использую Django-фильтры для запроса данных на основе диапазона (__lte, __gte). Как /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Zвернуть все данные , созданные до2020-02-14T09:15:52.329641Z

[
    {
        id: 1,
        created_at: 2020-02-03T19:30:57.868588Z,
        extra: {'load_time': 00, 'backlink': 0},
        rank: 0
    },
    ...
 ]

Есть ли способ , чтобы построить конечную точку для возврата агрегированных данных , сгруппированных по дням, недели, месяц и год на основе запроса Params я прохожу. Например, /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Z&group_by=monthвернется

[
    {
        created_at: 2020-01-01T00:00:00.000000Z,
        extra: {'load_time': 00, 'backlink': 0}, <- Aggregated Data 
        rank: 0                                    <- Aggregated Data
    },
    {
        created_at: 2020-02-01T00:00:00.000000Z,
        extra: {'load_time': 00, 'backlink': 0}, <- Aggregated Data 
        rank: 0                                    <- Aggregated Data 
    },
 ]

Вот мой текущий serializer.py

class AlexaViewSet(viewsets.ModelViewSet):
    queryset = Alexa.objects.all()
    filter_fields = {'created_at' : ['iexact', 'lte', 'gte']}
    http_method_names = ['get', 'post', 'head']

Я видел несколько фрагментов делает агрегацию, но ни один полностью не выполняет мои требования и не дает мне полное представление о теме.

Я новичок в Django и строительство аналитических информационных панели в целом, если есть какой-либо другой способ представления данных таких временных рядов, для потребления в фронтальных графиках, я был бы признателен за ваши предложения, что хорошо.

Задан 15/02/2020 в 08:48
пользователем
На других языках...                            


1 ответов

голоса
0

Прежде всего, класс AlexaViewSetне сериализатору но Viewset. Вы не указали класс сериализатора на этой Viewset поэтому я вам нужно указать , что.

С другой стороны, если вы хотите передать из параметров пользовательского запроса на URL , то вы должны переопределить listметод этого Viewset и разобрать строку запроса , переданную в requestобъекте , чтобы получить значение group_by, проверить его, а затем perfom агрегации ОТСИДЕТЬ ,

Еще одна проблема , которую я вижу, что вы также должны определить , что является агрегирование поле JSON, который не поддерживается в SQL , и это очень относительное, так что вы можете рассмотреть вопрос о реорганизации , как хранить информацию этого поля JSON , если вы хотите в perfom скоплений на полях внутри него. Я хотел бы предложить извлекая поля , которые вы хотите агрегировать из JSON (при хранении их в базе данных) и поместить их в столбце SQL отдельно , чтобы вы могли выполнить агрегирование позже. Клиент может также передать операцию агрегирования в качестве параметра запроса, например , aggregation=sumили aggregation=avg.

В простом случае, когда вам просто нужно в среднем рангов это должно быть полезным в качестве примера (можно добавить TruncQuarterи т.д.):

class AlexaViewSet(viewsets.ModelViewSet):
    serializer_class = AlexaSerializer
    queryset = Alexa.objects.all()
    filter_fields = {'created_at': ['iexact', 'lte', 'gte']}
    http_method_names = ['get', 'post', 'head']

    GROUP_CASTING_MAP = {  # Used for outputing the reset datetime when grouping
        'day': Cast(TruncDate('created_at'), output_field=DateTimeField()),
        'month': Cast(TruncMonth('created_at'), output_field=DateTimeField()),
        'week': Cast(TruncWeek('created_at'), output_field=DateTimeField()),
        'year': Cast(TruncYear('created_at'), output_field=DateTimeField()),
    }

    GROUP_ANNOTATIONS_MAP = {  # Defines the fields used for grouping
        'day': {
            'day': TruncDay('created_at'),
            'month': TruncMonth('created_at'),
            'year': TruncYear('created_at'),
        },
        'week': {
            'week': TruncWeek('created_at')
        },
        'month': {
            'month': TruncMonth('created_at'),
            'year': TruncYear('created_at'),
        },
        'year': {
            'year': TruncYear('created_at'),
        },
    }

    def list(self, request, *args, **kwargs):
        group_by_field = request.GET.get('group_by', None)
        if group_by_field and group_by_field not in self.GROUP_CASTING_MAP.keys():  # validate possible values
            return Response(status=status.HTTP_400_BAD_REQUEST)

        queryset = self.filter_queryset(self.get_queryset())

        if group_by_field:
            queryset = queryset.annotate(**self.GROUP_ANNOTATIONS_MAP[group_by_field]) \
                .values(*self.GROUP_ANNOTATIONS_MAP[group_by_field]) \
                .annotate(rank=Avg('rank'), created_at=self.GROUP_CASTING_MAP[group_by_field]) \
                .values('rank', 'created_at')

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

Для этих значений:

GET /alexa
[
    {
        "id": 1,
        "created_at": "2020-03-16T12:04:59.096098Z",
        "extra": "{}",
        "rank": 2
    },
    {
        "id": 2,
        "created_at": "2020-02-15T12:05:01.907920Z",
        "extra": "{}",
        "rank": 64
    },
    {
        "id": 3,
        "created_at": "2020-02-15T12:05:03.890150Z",
        "extra": "{}",
        "rank": 232
    },
    {
        "id": 4,
        "created_at": "2020-02-15T12:05:06.357748Z",
        "extra": "{}",
        "rank": 12
    }
]
GET /alexa/?group_by=day
[
    {
        "created_at": "2020-02-15T00:00:00Z",
        "extra": null,
        "rank": 102
    },
    {
        "created_at": "2020-03-16T00:00:00Z",
        "extra": null,
        "rank": 2
    }
]
GET /alexa/?group_by=week
[
    {
        "created_at": "2020-02-10T00:00:00Z",
        "extra": null,
        "rank": 102
    },
    {
        "created_at": "2020-03-16T00:00:00Z",
        "extra": null,
        "rank": 2
    }
]

GET /alexa/?group_by=month
[
    {
        "created_at": "2020-02-01T00:00:00Z",
        "extra": null,
        "rank": 102
    },
    {
        "created_at": "2020-03-01T00:00:00Z",
        "extra": null,
        "rank": 2
    }
]
GET /alexa/?group_by=year
[
    {
        "created_at": "2020-01-01T00:00:00Z",
        "extra": null,
        "rank": 77
    }
]
Ответил 15/02/2020 в 20:34
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more