Endpoint with very slow response with DRF when receiving a parameter - python

I am using Django REST framework for my API and I start using filters, depending on the value, it make various count, but it take at least 3 minutes everytime I make a response.
The serealizer use the context['request'] to get the url parameter and filter for the result
class CheetosSerializer(serializers.ModelSerializer):
total_cheetos = serializers.SerializerMethodField()
cheetos_on_sale = serializers.SerializerMethodField()
cheetos_on_stock = serializers.SerializerMethodField()
class Meta:
model = Cheetos
fields = (
'total_cheetos'
,'cheetos_on_sale'
,'cheetos_on_stock'
)
read_only_fields = fields
def get_total_cheetos(self,obj):
request_object = self.context['request']
customer_id = request_object.query_params.get('Shop__id_Cat_Customer')
if customer_id is None:
return Cheetos.objects.filter(Estatus=3).count()
else:
return Cheetos.objects.filter(Estatus=3,Shop__id_Cat_Customer = customer_id).count()
def get_cheetos_on_sale(self,obj):
request_object = self.context['request']
customer_id = request_object.query_params.get('Shop__id_Cat_Customer')
if customer_id is None:
return Cheetos.objects.filter(Estatus=3, id_Cat = 1).count()
else:
return Cheetos.objects.filter(Estatus=3, id_Cat = 1,Shop__id_Cat_Customer = customer_id).count()
def get_cheetos_on_stock(self,obj):
request_object = self.context['request']
customer_id = request_object.query_params.get('Shop__id_Cat_Customer')
if customer_id is None:
return Cheetos.objects.filter(Estatus=3, id_Cat = 2).count()
else:
return Cheetos.objects.filter(Estatus=3, id_Cat = 2,Shop__id_Cat_Customer = customer_id).count()
On the view is where I set the filterset parameter
class CheetosView(DefaultViewSetMixin):
filterset_fields = ['Shop__id_Cat_Customer']
queryset = Cheetos.objects.all()
serializer_class = CheetosSerializer
And I use postman to validate the data, and here it says the time it takes with the correct values I´m looking for:
Postman Result
Is there a way to make this much better?

Related

Calling viewset from another route

I use Django rest-framework which fetch the items from tables and return serialized json, when calling like this below
localhost/api/mixs?id=12
Source code.
class MixViewSet(viewsets.ModelViewSet):
serializer_class = MixSerializer
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
filter_fields = ["id","user"]
filterset_fields = ['id']
search_fields = ['id']
def list(self,request,*args,**kwargs):
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
custom_data = {
'items': serializer.data
}
custom_data.update({
'meta':{"api":"Mix"}
})
return Response(custom_data)
def get_queryset(self):
queryset = Mix.objects.all()
ids = self.request.query_params.get('ids')
if ids is not None:
id_arr = ids.split(',')
if len(id_arr) > 0:
queryset = queryset.filter(id__in=id_arr)
u_key = self.request.query_params.get('u_key')
if u_key is not None:
queryset = queryset.filter(u_key=u_key)
return queryset
Now, I want to use this function from another method.
For example
def createMix(request):
#do something and make Mix
m = Mix(detail={},user=1)
m.save()
print(m.id) ### for example 13
#Now I want to do the equivalent thing
#to `localhost/api/mixs?id=13`
# returning the data id=13
obj = Mix.objects.get(id=m.id)
response = MixSerializer(obj)
print(response)
return Response(response)
#AssertionError: You passed a Serializer instance as data, but probably meant to pass serialized `.data` or `.error`. representation
When calling this url
localhost/myapp/createsong
The program do something and insert data in Mix table.
then do the equivalent thing to localhost/api/mixs?id=13
Is it possible?
Or my idea is correct?
You can probaly do something like this. There is nothing restricts you from using test client as part of code.
from django.test.client import Client
c = Client()
article = c.post('/api/mixes', {
'id' : 13,
})

DRF pass the URL parameter into serizalizer

Can we pass the URL queryset parameter into serizalizer to have filitering/calculation?
here is my endpoint: ?start_date=20210312&end_date=20210317
herer is my viewset:
class BrandChartsViewSet(ModelViewSet):
serializer_class = BrandChartsSerializer
pagination_class = BrandChartsPagination
queryset = Brand.objects.all()
def get_queryset(self):
start_date = self.request.query_params.get('start_date',None)
end_date = self.request.query_params.get('end_date',None)
if start_date is None:
raise InvalidInputError()
if end_date is None:
raise InvalidInputError()
start_date_obj = datetime.strptime(start_date,'%Y%m%d')
end_date_obj = datetime.strptime(end_date,'%Y%m%d')
serializer = BrandChartsSerializer(start_date_obj,end_date_obj)
queryset = Brand.objects.all()
return queryset
here is my serizalizer:
class BrandChartsSerializer(serializers.ModelSerializer):
rankings = serializers.SerializerMethodField()
instagram = IGSerializer(allow_null=True)
facebook = FBSerializer(allow_null=True)
hashtags = BrandHashtagSerializer(allow_null=True, many=True)
# rename the json field
brand_uid = serializers.IntegerField(source='id')
brand_name = serializers.CharField(source='name')
origin = serializers.CharField(source="country")
class Meta:
model = Brand
fields = [
"brand_uid",
"brand_name",
"origin",
"rankings",
"instagram",
"facebook",
"hashtags",
]
def get_rankings(self, instance):
rank = instance.rankings.all().filter(created__gte=start_date_obj,
created__lte=end_date_obj
).order_by('created')
return BrandRankSerializer(rank, allow_null=True, many=True).data
and it will have Unresolved reference 'start_date_obj' in get_rankings, however I tried to passed the parameter into serizalizer by class BrandChartsSerializer(serializers.ModelSerializer,start_date_obj,end_date_obj)
It still giving error.
Due to my function design, I think it could not use DRF filter to handle it, could I just pass the parameter to serizalizer to do filitering? (I need to filter the rankings model in range and calculate the sum than return)
We can make use of context in such cases.
Try this.
Update the get_rankings method.
def get_rankings(self, instance):
start_date_obj = self.context.get("request").query_params.get("start_date")
end_date_obj = self.context.get("request").query_params.get("end_date")
rank = instance.rankings.all().filter(created__gte=start_date_obj,
created__lte=end_date_obj
).order_by('created')
return BrandRankSerializer(rank, allow_null=True, many=True).data

Return a response with a list of serializers Django REST Framework

I'm coding some backend software for a second-hand selling app using Django and DjangoRestFramework. Right now, I'm trying to send a Response object that contains a list of products, but I seem not to be able to return an actual list of products, as I'm getting an error saying
ListSerializer is not JSON serializable.
I've tried both using the serializer constructor like this:
ProductoSerializer(products, many=True)
And by creating a list of ProductoSerializer.data and then creating the Response object with that.
Here's the serializers that I'm using:
class UserSerializer(serializers.HyperlinkedModelSerializer):
ciudad = serializers.SerializerMethodField()
conectado = serializers.SerializerMethodField()
class Meta:
model = Usuario
fields = ('uid', 'nombre', 'ciudad', 'conectado')
def get_ciudad(self, obj):
geolocator = Nominatim(user_agent="bookalo")
location = geolocator.reverse(str(obj.latitud_registro) + ',' + str(obj.longitud_registro))
return location.raw['address']['city']
def get_conectado(self, obj):
ahora = timezone.now()
result = relativedelta(ahora, obj.ultima_conexion)
return result.days == 0 and result.hours == 0 and result.months == 0 and result.years == 0 and result.minutes < 5
class TagSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tag
fields = ('nombre')
class MultimediaSerializer(serializers.HyperlinkedModelSerializer):
contenido_url = serializers.SerializerMethodField()
class Meta:
model = ContenidoMultimedia
fields = ('contenido_url', 'orden_en_producto')
def get_contenido_url(self, obj):
return obj.contenido.url
class MiniProductoSerializer(serializers.HyperlinkedModelSerializer):
contenido_multimedia = serializers.SerializerMethodField()
class Meta:
model = Producto
fields = ('nombre', 'precio', 'estado_venta', 'contenido_multimedia')
def get_contenido_multimedia(self, obj):
contenido = ContenidoMultimedia.objects.get(producto=obj.pk, orden_en_producto=0)
return MultimediaSerializer(contenido)
class ProductoSerializer(serializers.HyperlinkedModelSerializer):
vendido_por = UserSerializer(read_only=True)
tiene_tags = TagSerializer(many=True, read_only=True)
contenido_multimedia = serializers.SerializerMethodField()
valoracion_media_usuario = serializers.SerializerMethodField()
class Meta:
model = Producto
fields = ('nombre', 'precio', 'estado_producto', 'estado_venta', 'latitud', 'longitud', 'tipo_envio', 'descripcion', 'vendido_por', 'tiene_tags', 'num_likes', 'contenido_multimedia')
def get_contenido_multimedia(self, obj):
contenido = ContenidoMultimedia.objects.filter(producto=obj.pk).order_by('orden_en_producto')
return MultimediaSerializer(contenido, many=True)
def get_valoracion_media_usuario(self, obj):
return Usuario.objects.get(pk=obj.vendido_por).media_valoraciones
class ValidacionEstrellaSerializer(serializers.HyperlinkedModelSerializer):
usuario_que_valora = UserSerializer(read_only=True)
producto_asociado = serializers.SerializerMethodField()
class Meta:
model = ValidacionEstrella
fields = ('estrellas', 'comentario', 'timestamp', 'usuario_que_valora', 'producto_asociado')
def get_producto_asociado(self, obj):
producto = Producto.objects.get(pk=obj.producto)
return MiniProductoSerializer(producto)
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
usuario_valorado_estrella = serializers.SerializerMethodField()
productos_favoritos = serializers.SerializerMethodField()
class Meta:
model = Usuario
fields = ('uid', 'nombre', 'esta_baneado', 'usuario_valorado_estrella', 'producto_del_usuario')
def get_usuario_valorado_estrella(self, obj):
validaciones = ValidacionEstrella.objects.filter(usuario_valorado=obj.pk).order_by('-timestamp')
return ValidacionEstrellaSerializer(validaciones, many=True, read_only=True)
def get_productos_favoritos(self, obj):
favoritos = Producto.objects.filter(le_gusta_a__in=[obj.pk])
return ProductoSerializer(favoritos, many=True, read_only=True)
class ReportSerializer(serializers.HyperlinkedModelSerializer):
#usuario_reportado = serializers.SerializerMethodField()
usuario_reportado = UserSerializer(read_only=True)
class Meta:
model = Report
fields = ('usuario_reportado', 'causa')
And here's the views.py function that I'm trying to code:
#api_view(['POST'])
#permission_classes((permissions.AllowAny,))
def SearchProduct(request, format=None):
if request.method != 'POST':
return Response(status=status.HTTP_400_BAD_REQUEST)
preposiciones = ['a','ante','bajo','cabe','con','contra','de','desde','en','entre',
'hacia','hasta','para','por','segun','sin','so','sobre','tras']
try:
search = request.POST.get('busqueda')
except:
return Response(status=status.HTTP_404_NOT_FOUND)
products = Producto.objects.none()
for word in search.split():
if word not in preposiciones:
productos_palabra = Producto.objects.filter(nombre__contains=word)
products = products | productos_palabra
products.distinct()
product_list = []
for prod in products:
product_list.append(ProductoSerializer(prod).data)
return Response({'productos': product_list}, status=status.HTTP_200_OK)
I'm using a request object because I also have to server a WebPage, and not only a mobile app, with the same function (the webpage part is still not coded though).
It should return all the products that contain at least one of the words from the user's search, and it all should be structured based on the ProductoSerializer object, but for some reason, it's outputting that error and I'm not quite sure how to fix it.
Thanks in advance, and if you need any extra information which I've missed, please do ask for it... It's been a long day and I probably missed something.
Seems like when you use SerializerMethodField you return serializer instance but not it's data:
For instance:
contenido_multimedia = serializers.SerializerMethodField()
def get_contenido_multimedia(self, obj):
contenido = ContenidoMultimedia.objects.filter(producto=obj.pk).order_by('orden_en_producto')
return MultimediaSerializer(contenido, many=True).data # <-- here try to add .data
It should be changed for all SerializerMethodField methods.

How to export a PDF from an endpoint in Django Rest Framework

I'm currently setting up a GET request from my endpoint to export a PDF file based on the filters(django_filters) on my ModelViewSet and the PDF's design will based on the template_path provided. And I'm fairly new on using DRF, so I'm new to some things.
I've tried using easy_pdf for my ModelViewSet, but it doesn't show on the endpoint's GET request. And also no error was provided in the logs, I assume I'm doing something really wrong on this.
This is my Filter
class CarrierFilter(django_filters.FilterSet):
date_app_rec__gte = DateFilter(field_name='date_app_rec', lookup_expr='gte')
date_app_rec__lte = DateFilter(field_name='date_app_rec', lookup_expr='lte')
date_sample_rec__gte = DateFilter(field_name='date_sample_rec', lookup_expr='gte')
date_sample_rec__lte = DateFilter(field_name='date_sample_rec', lookup_expr='lte')
date_of_qca__gte = DateFilter(field_name='date_of_qca', lookup_expr='gte')
date_of_qca__lte = DateFilter(field_name='date_of_qca', lookup_expr='lte')
date_created__gte = DateFilter(field_name='date_created', lookup_expr='gte')
date_created__lte = DateFilter(field_name='date_created', lookup_expr='lte')
patient_name = CharFilter(field_name='patient_name', lookup_expr='icontains')
class Meta:
model = Carrier
fields = ('date_app_rec__gte', 'date_app_rec__lte',
'date_sample_rec__gte', 'date_sample_rec__lte',
'date_of_qca__gte', 'date_of_qca__lte',
'date_created__gte', 'date_created__lte',
'patient_name',)
And this is my ModelViewSet
class CarrierViewSet(XLSXFileMixin, PDFTemplateResponseMixin, ModelViewSet):
serializer_class = CarrierSerializer
permission_classes = (IsAuthenticated,)
parser_classes = (MultiPartParser,)
filename = 'carrier-reports.xlsx'
pdf_filename = 'carrier-report.pdf'
filter_class = (CarrierFilter)
filterset_fields = ('patient_name', 'promo_code')
search_fields = ('patient_name', 'promo_code', 'insurance_verified_tsg_verification')
def get_queryset(self):
user = self.request.user
position = self.request.user.position
if position == 'Manager':
queryset = Carrier.objects.filter(manager__name=user)
else:
queryset = Carrier.objects.filter(agent__name=user)
return queryset
def get(self, request, format=None):
carrier = Carrier.objects.all()
template_path = 'carrier/carrier_print.html'
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="carrier-report.pdf"'
html = render_to_string(template_path, {'carrier': carrier})
print(html)
pisaStatus = pisa.CreatePDF(html, dest=response)
return response
What I want to do is that the pdf should be in the GET request of that endpoint, same scenario as the drf_renderer_xlsx when using the xlsx from the GET. Or is there another way around on doing this?

Tastypie 'Search' Only Returns Resource Name

I'm trying to create a function that filters objects by a ForeingKey field (or rather the object being referenced in the field). I wrote the function which I have included below, which is based off the Tastypie Cookbook's Adding Search Functionality. The function does work in that it returns the objects, however it only returns the objects' names as a string.
This is the result of calling the function:
{'receipt_items': [<ReceiptItem: Item1>, <ReceiptItem: Item2>, <ReceiptItem: Item3>]}
Here is my resource:
class ReceiptItemResource(ModelResource):
receipt = fields.ToOneField(ReceiptResource, 'receipt', full = True)
class Meta:
queryset = ReceiptItem.objects.all()
serializer = Serializer()
allowed_methods = ['get', 'post', 'put', 'delete']
always_return_data = True
def prepend_urls(self):
return [
url(r'^(?P<resource_name>%s)/filter%s$' % (self._meta.resource_name, trailing_slash()), self.wrap_view('filter_by_receipt'), name = 'filter_by_receipt'),
]
def filter_by_receipt(self, request, **kwargs):
data = self.deserialize(request, request.body, format = request.META.get('CONTENT_TYPE', 'application/json'))
receipt_id = data.get('receipt_id', '')
print receipt_id
receipt = Receipt.objects.get(id = receipt_id)
receipt_items = ReceiptItem.objects.filter(receipt = receipt)
item_list = {
'receipt_items' : receipt_items,
}
print item_list
return self.create_response(request, receipt_items)
#return super(ReceiptItemResource, self).get_object_list(request).filter(receipt = receipt)
def get_object_list(self, request):
user = request.user
member = user.member
owner = member.owner
return super(ReceiptItemResource, self).get_object_list(request).filter(owner = owner)
Ideally I would like this function to return the full object details in JSON. Is there any way to make this happen?
I have looked into Tastypie's resource filtering, however I don't believe this would work since the field I am trying to filter by is a ForeignKey.
All help is greatly appreciated, thank you.
You should not need to do this. You can filter on foreignKey by default using the filtering option
from tastypie.resources import ALL, ALL_WITH_RELATIONS
class RecieptResource(ModelResource):
class Meta:
fields = ["id"]
filtering = {
"id": ALL
}
class ReceiptItemResource(ModelResource):
receipt = fields.ToOneField(ReceiptResource, 'receipt', full = True)
class Meta:
queryset = ReceiptItem.objects.all()
allowed_methods = ['get', 'post', 'put', 'delete']
always_return_data = True
filtering = {
"reciept": ALL_WITH_RELATIONS
}
Now the following should url (depending on how you have yours configured) should give you what you want
/api/v1/reciept-item/?receipt__id=1&format=json

Categories