DRF pass the URL parameter into serizalizer - python

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

Related

AttributeError: 'Serializer' object has no attribute 'Meta' in django rest framework

I am using serializers.Serializer instead of ModelSerializer which doesn't require Meta class but it keep saying object has no attribute Meta. Iam not sure what is the issue but when I run the localserver, the main page gives error saying api fetch error and in the terminal it says AttributeError: 'Serializer' object has no attribute 'Meta'.
My view:
class ClassView(viewsets.ModelViewSet):
queryset = Class.objects.all().order_by('-created_at')
serializer_class = ClassSerializer
serializer_action_classes = {
'get_all_students_of_a_class': ClassDetailSerializer,
}
# .annotate(total_students=Count('students_in_class'))
def get_serializer_class(self):
"""
returns a serializer class based on the action
that has been defined.
"""
try:
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
return super(ClassView, self).get_serializer_class()
def get_all_students_of_a_class(self,request,pk=None):
"""
returns a class details with all the students signed up for the
class along with the subject.
"""
id = pk
if self.is_teacher(id):
online_classes = get_object_or_404(Class, id=id)
students = Student.objects.filter(online_class__id=id)
subject_details = Subject.objects.get(online_class__id=id)
total_students_in_class = online_classes.total_students_in_class
created_at = online_classes.created_at
updated_at = online_classes.updated_at
data = {
"teacher": self.get_teacher_instance(),
'total_students_in_class': total_students_in_class,
"students_in_class": students,
"subject": subject_details,
'created_at': created_at,
'last_updated_at': updated_at,
}
serializer_class = self.get_serializer_class()
serializer = serializer_class(data)
return Response(serializer.data, status=status.HTTP_200_OK)
My serializer:
class ClassDetailSerializer(serializers.Serializer):
teacher = serializers.StringRelatedField()
subject = SubjectLevelSerializer()
total_students_in_class = serializers.ReadOnlyField()
students_in_class = serializers.StringRelatedField(many=True)
created_at = serializers.DateTimeField(read_only=True)
last_updated_at = serializers.DateTimeField(read_only=True)
My url:
path("class/<int:pk>/",teacher.ClassView.as_view({"get": "get_all_students_of_a_class","delete":"destroy"}),
),
However it works and I can perform action if I go to localhost/admin and other api calls from localhost.
Add a Meta class with model = YourModel
class ExampleDetailSerializer(serializers.Serializer):
employee = serializers.StringRelatedField()
person = PersonSerializer()
class Meta:
model = Example # model name
fields = ('__all__')
Try to use "ViewSet" instead of "ModelViewSet". And while using ViewSet make sure to define list, create, etc. function by your own.

Endpoint with very slow response with DRF when receiving a parameter

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?

Django REST, How to display every 5 element in a response?

Now the serializer displays all the data from the CoinCosts model in response (price,timestamp), but only every 5 element is needed, how to do it? Thanks
I need something like Entry.objects.all()[::5], but I don’t know how to do this with my code.
My code now:
serializers.py
class CoinCostsSerializer(serializers.ModelSerializer):
class Meta:
fields = ('price', 'timestamp')
model = CoinCosts
class CoinSerializer(serializers.ModelSerializer):
class Meta:
fields = ('symbol', 'crr', 'costs')
model = Coins
costs = CoinCostsSerializer(source='filtered_coincosts', many=True)
views.py
class DateTimeGteFilter(filters.IsoDateTimeFilter):
def filter(self, qs, value):
if value != None:
return qs.prefetch_related(Prefetch('coincosts_set', to_attr='filtered_coincosts', queryset=CoinCosts.objects.filter(timestamp__gte=value)
)
)
else:
return qs
class CoinCostFilterSet(filters.FilterSet):
timestamp = DateTimeGteFilter()
class Meta:
model = Coins
fields = {
'symbol': ['exact'],
}
You can use try and except like this in views.py in your def:
try:
obj = Entry.objects.all()[::5]
except IndexError:
obj = None

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.

Django rest framework add more data when serialize many object

I would like to add an additional field for user's details. But it has to use another value outside of database fields.
For more clearly like this
model:
class User(models.Model):
id= models.AutoField(primary_key=True)
last_name= models.CharField(max_length=20)
first_name=models.CharField(max_length=20)
role_id = models.IntegerField()
serializer:
class UserSerializer(serializers.ModelSerializer):
display_summary = serializers.SerializerMethodField()
login_user_id = serializers.IntegerField(required=False)
class Meta:
model = User
fields = ("id","last_name","first_name", "display_summary", "login_user_id")
def get_display_summary(self, obj):
login_id = self.validated_data.get('login_user_id', None)
login_user = User.objects.filter(pk=login_id).first()
if obj.role_id==2 and login_user.role_id==1:
return 1
return 0
So in my views, when getting just one user, it's all ok:
#api_view(['GET'])
#login_required
def get_user(request, login_user, user_id):
serializer = UserSerializer(User.objects.get(pk=user_id), data={'login_user_id': login_user.id})
if serializer.is_valid():
result = serializer.data
return Response(result, status=status.HTTP_200_OK)
#result:
#{
# "id": 2,
# "last_name": "V",
# "first_name": "Lich",
# "role_id": 2,
# "display_summary": 1
#}
But when I need to return a list, how can I add additional data (login_user_id)?
This is not working:
users = User.objects.filter(last_name__icontains='v')
result_serializer = UserSerializer(users, data={'login_user_id': login_user.id}, many=True)
return result_serializer.data
The error occur say that it's looking for a list, not a dict for inputted param.
Based on #Hai Lang 's idea, I made a new namedtuple to add more info. Something like this:
Serializer:
No use ModelSerializer, use normal Serializer with field I would like to show on this view
class UserSummarySerializer(serializers.Serializer):
user_id = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
display_summary = serializers.SerializerMethodField()
def validate(self, data):
#some validation if needed
return data
def get_user_id(self, obj):
return obj.user.user_id
def get_last_name(self, obj):
return obj.user.last_name
def get_first_name(self, obj):
return obj.user.first_name
def get_display_summary(self, obj):
login_user = obj.login_user
if obj.role_id==2 and login_user.role_id==1:
return 1
return 0
then in my views:
userSummary = namedtuple('userSummary ', ('user', 'login_user'))
users = User.objects.filter(last_name__icontains='v')
objs = [userSummary(u, login_user) for u in users]
result_serializer = UserSerializer(objs, many=True)
return result_serializer.data

Categories