Search code examples
pythondjangorestdjango-viewsdjango-urls

Merge multiple views with same functionality in one view


I'm trying to define 4 GET endpoints (max,min,sum,avg) to access by REST. I did this but making 4 views, and I want to know if I can define the 4 methods in a single view. I can't specify through the url which GET to hit. It's possible?

my urls.py set

urlpatterns =[
path('medidores/', MedidorView.as_view()),
path('mediciones/', MedicionView.as_view()),
path('medicion_max/<str:id>', MedicionMaxView.as_view()),
path('medicion_min/<str:id>', MedicionMinView.as_view()),
path('medicion_total/<str:id>', MedicionTotalView.as_view()),
path('medicion_prom/<str:id>', MedicionPromedioView.as_view())
]

my view.py set

class MedicionMaxView(View):
    def get(self, request, id):
        return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Max('consumo_kwh')))

class MedicionMinView(View):
    def get(self, request, id):
        return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Min('consumo_kwh')))

class MedicionTotalView(View):
    def get(self, request, id):
        return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Sum('consumo_kwh')))

class MedicionPromedioView(View):
    def get(self, request, id):
        return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Avg('consumo_kwh')))

Separate by name in urls but not have effect

UPDATE:

I put my urls like you

    urlpatterns =[
    path('medidores/', MedidorView.as_view()),
    path('mediciones/', MedicionView.as_view()),
    re_path(r'medicion/(?P<aggregation>(max|min|sum|avg))/<str:id>', MedicionView.as_view()),
    path('medicion_max/<str:id>', MedicionMaxView.as_view()),
    path('medicion_min/<str:id>', MedicionMinView.as_view()),
    path('medicion_total/<str:id>', MedicionTotalView.as_view()),
    path('medicion_prom/<str:id>', MedicionPromedioView.as_view())
]

But i cant match results

I don't know Stack Overflow rules but could share my git repository if needed. (although the code does not differ from what we were seeing here) PS: I'm using google translate, sorry


Solution

  • Yes, you can!

    as_view() accepts parameters, so you can have one view like:

    class MedicionView(View):
        aggregation=""
        def get(self, request, id):
            if self.aggregation == 'max':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Max('consumo_kwh')))
            elif self.aggregation == 'min':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Min('consumo_kwh')))
            elif self.aggregation == 'sum':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Sum('consumo_kwh')))
            elif self.aggregation == 'avg':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Avg('consumo_kwh')))
            else:
                # you can handle it
    

    Side note: you can make this get() method more clean. For example, move the common calls one level up like Medicion.objects.filter(medidor=id)

    Now, can pass the needed aggregation way as parameter to as_view() with your URLs like:

    urlpatterns =[
        path('medidores/', MedidorView.as_view()),
        path('mediciones/', MedicionView.as_view()),
        path('medicion_max/<str:id>', MedicionView.as_view(aggregation='max')),
        path('medicion_min/<str:id>', MedicionView.as_view(aggregation='min')),
        path('medicion_total/<str:id>', MedicionView.as_view(aggregation='sum')),
        path('medicion_prom/<str:id>', MedicionView.as_view(aggregation='avg'))
    ]
    

    Better way:

    You can even make it more clean by having one URL pattern for all of them by having the aggregation type as a URL parameter like:

    class MedicionView(View):
        def get(self, request, aggregation, id):
            if aggregation == 'max':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Max('consumo_kwh')))
            elif aggregation == 'min':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Min('consumo_kwh')))
            elif aggregation == 'sum':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Sum('consumo_kwh')))
            elif aggregation == 'avg':
                return JsonResponse(Medicion.objects.filter(medidor=id).aggregate(Avg('consumo_kwh')))
            else:
                # you can handle it
    

    Now, you can have one URL pattern for them using regular expression:

    urlpatterns =[
        path('medidores/', MedidorView.as_view()),
        path('mediciones/', MedicionView.as_view()),
        path(r'medicion/(?P<aggregation>(max|min|sum|avg))/<str:id>', MedicionView.as_view())
    ]
    

    Update:

    If you are following the second solution, don't forget to add the character r at the beginning of the URL patterns that follow a regex pattern as I did above.