Django filtrare i dati timestamp giorno GROUP BY, settimana, mese, anno

voti
30

Ho un app Django (DRF) in cui memorizzare dati TimeSeries periodici basati sulla risposta API. Ecco il mio 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)

Sto usando django-filtri per i dati di query basate su una serie (__lte, __gte). Come /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Zrestituire tutti i dati creati prima2020-02-14T09:15:52.329641Z

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

C'è un modo per costruire un endpoint per restituire i dati aggregati raggruppati per giorno, settimana, mese e anno in base ai parametri di query che passare. Ad esempio, /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Z&group_by=monthsarebbe tornato

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

Ecco il mio serializer.py corrente

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

Ho visto parecchi frammenti che fanno aggregazione ma nessuno soddisfare completamente le mie esigenze e non mi dà un'idea completa sull'argomento.

Sono nuovo di Django e costruzione di analisi cruscotti in generale, se ci sono altri modi di rappresentare i dati tali TimeSeries per il consumo nei grafici di front-end, apprezzerei i vostri suggerimenti a quello.

È pubblicato 15/02/2020 alle 08:48
dall'utente
In altre lingue...                            


1 risposte

voti
0

Prima di tutto, la classe AlexaViewSetnon è un serializzatore, ma un ViewSet. Lei non ha specificato la classe serializer su quel ViewSet quindi è necessario specificare che.

D'altra parte, se si vuole passare un parametro query personalizzata sull'URL allora si dovrebbe eseguire l'override del listmetodo di questa ViewSet e analizzare la stringa di query passata in requestoggetto per recuperare il valore di group_by, convalidare, e poi perfom l'aggregazione youself .

Un altro problema che vedo è che hai bisogno anche di definire ciò che è quello di aggregare un campo JSON, che non è supportato in SQL ed è molto relativo, quindi si consiglia di prendere in considerazione la riprogettazione come si memorizzano le informazioni di questo campo JSON, se si desidera ad aggregazioni perfom sui campi al suo interno. Vorrei suggerire l'estrazione dei campi che si desidera aggregare dal JSON (quando la loro memorizzazione nel database) e metterli in una colonna di SQL separatamente, in modo è possibile eseguire aggregazioni più tardi. Il client può anche passare all'operazione agrégation come un parametro di ricerca, ad esempio aggregation=sumo aggregation=avg.

In un caso semplice, in cui è sufficiente la media dei ranghi questo dovrebbe essere utile come un esempio (si potrebbe aggiungere 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)

Per questi valori:

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
    }
]
Risposto il 15/02/2020 a 20:34
fonte dall'utente

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