Django datos de marca de tiempo del filtro días GROUP BY, semana, mes, año

votos
30

Tengo una aplicación django (DRF) en la que almacenar datos de series temporales periódicos basados ​​en la respuesta de la API. Aquí está mi 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)

Estoy usando Django filtros para datos de consulta sobre la base de un rango (__lte, __gte). Al igual que /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Zdevolver todos los datos creados antes2020-02-14T09:15:52.329641Z

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

¿Hay una manera de construir un punto final para devolver los datos agregados agrupados por día, semana, mes y año en base a los Parámetros de consulta que pase. Por ejemplo, /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Z&group_by=monthvolvería

[
    {
        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 
    },
 ]

Aquí está mi actual serializer.py

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

He visto varios fragmentos que hacen la agregación pero ninguno cumplir por completo mis necesidades ni me da una idea completa sobre el tema.

Soy nuevo en Django y la construcción de tableros de instrumentos de análisis en general, si hay alguna otra manera de representar los datos de dichas series temporales para el consumo en los gráficos de front-end, apreciaría sus sugerencias para eso también.

Publicado el 15/02/2020 a las 08:48
por usuario
En otros idiomas...                            


1 respuestas

votos
0

En primer lugar, la clase AlexaViewSetno es un serializador sino un ViewSet. No se especificó la clase serializador en ese ViewSet por lo que es necesario especificar que.

Por otro lado, si usted desea pasar un parámetro de consulta personalizada en la URL a continuación, debe anular el listprocedimiento de esta ViewSet y analizar la cadena de consulta que se pasa en el requestobjeto para recuperar el valor de group_by, validar, y después el proceso de la agregación youself .

Otro problema que veo es que también hay que definir qué es agregar un campo JSON, que no se admite en SQL y es muy relativo, por lo que es posible que desee considerar el rediseño de cómo se almacena la información de este campo JSON si quieres a agregaciones perfom en campos en su interior. Yo sugeriría la extracción de los campos que desee agregar desde el JSON (al almacenarlos en la base de datos) y los puso en una columna de SQL por separado por lo que podría realizar agregaciones más tarde. El cliente también podría pasar a la operación de agregación como parámetro de consulta, por ejemplo aggregation=sum, o aggregation=avg.

En un caso sencillo, en el que sólo necesita el promedio de los rangos esto debería ser útil como un ejemplo (se podría añadir TruncQuarter, etc.):

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)

Para estos valores:

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
    }
]
Respondida el 15/02/2020 a las 20:34
fuente por usuario

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