Django filtre les données d'horodatage GROUP BY jour, semaine, mois, année

voix
30

Je une application django (DRF) dans laquelle I stocker des données de séries chronologiques périodiques sur la base de la réponse de l'API. Voici mon 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)

J'utilise des filtres django à des données de requête à partir d'une plage (__lte, __gte). Comme /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Zretourner toutes les données créées avant2020-02-14T09:15:52.329641Z

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

Est - il possible de construire un point final pour renvoyer des données agrégées regroupées par jour, semaine, mois et année sur la base des paramètres de requête que je passe. Par exemple, /api/alexa/?created_at__lte=2020-02-14T09:15:52.329641Z&group_by=monthrenverrait

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

Voici mon serializer.py actuel

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

Je l'ai vu plusieurs extraits qui font l'agrégation, mais pas tout à fait remplir mes exigences, ni me donner une idée sur le sujet.

Je suis nouveau à Django et tableaux de bord du bâtiment d'analyse en général, s'il y a une autre façon de représenter ces données de séries chronologiques pour la consommation sous forme de graphiques front-end, je vous serais reconnaissant de vos suggestions aussi.

Créé 15/02/2020 à 08:48
utilisateur
Dans d'autres langues...                            


1 réponses

voix
0

Tout d' abord, la classe AlexaViewSetn'est pas un sérialiseur mais ViewSet. Vous n'avez pas spécifié la classe sérialiseur sur cette ViewSet donc je vous dois préciser que.

De l'autre côté, si vous voulez passer un param de requête personnalisée sur l'URL, vous devez remplacer la listméthode de cette ViewSet et analyser la chaîne de requête passée dans l' requestobjet pour récupérer la valeur de group_by, valider et perfom puis l'agrégation youself .

Un autre problème que je vois que vous est que aussi besoin de définir ce qui est d'agréger un champ JSON, qui ne sont pas pris en charge dans SQL et il est très relatif, vous voudrez peut - être réfléchir à la refonte comment stocker les informations de ce champ JSON si vous voulez à agrégations perfom sur les champs à l' intérieur. Je suggère d' extraire les champs que vous souhaitez agréger du JSON (lors de leur stockage dans la base de données) et les mettre dans une colonne SQL séparément pour que vous puissiez effectuer des agrégations plus tard. Le client peut également passer l'opération en tant que paramètre agregation de requête, par exemple aggregation=sumou aggregation=avg.

Dans un cas simple, où vous avez juste besoin de la moyenne des rangs cela devrait être utile comme un exemple (vous pouvez ajouter 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)

Pour ces valeurs:

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
    }
]
Créé 15/02/2020 à 20:34
source utilisateur

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