How to assemble different serializers to a Json response in Django RFW? - python

This is my serializers.py,
class MalbSerializer(serializers.ModelSerializer):
class Meta:
model = malb
fields = ('zoning', 'zoningdesc', )
class MasrSerializer(serializers.ModelSerializer):
class Meta:
model = masr
fields = ('solddate', 'soldprice', )
class MataSerializer(serializers.ModelSerializer):
class Meta:
model = mata
fields = ('assessyear', 'landvalue', )
class TotalSerializer(serializers.ModelSerializer):
LandBuilding = serializers.SerializerMethodField()
SalesRecord = serializers.SerializerMethodField()
TaxAssessment = serializers.SerializerMethodField()
def get_LandBuilding(self, number):
queryset_lb = malb.objects.filter(maid=number)
serializer = MalbSerializer(queryset_lb, many=True)
return serializer.data
def get_SalesRecord(self, number):
queryset_sr = masr.objects.filter(maid=number)
serializer = MasrSerializer(queryset_sr, many=True)
return serializer.data
def get_TaxAssessment(self, number):
queryset_ta = mata.objects.filter(maid=number)
serializer = MataSerializer(queryset_ta, many=True)
return serializer.data
class Meta:
fields = ('LandBuilding', 'SalesRecord', 'TaxAssessment', )
I want to assemble these three serializers to one serializer in TotalSerializer, But it has an error:
Class TotalSerializer missing "Meta.model" attribute
I don't know add which models to here, because I have already add models in MalbSerializer, MasrSerializer, MataSerializer.
So How can I do to show MalbSerializer, MasrSerializer, MataSerializer together in TotalSerializer?

TotalSerializer should subclass serializers.Serializer, not serializers.ModelSerializer.

Related

how to create model instance in drf serializers

I am new to DRF. I want to get saved the model.
In models.py, PackageDetails and PhysicalDetail have foreignkey relationship to Member
My serializers.py is as follows:
from rest_framework import serializers
from .models import Member, PackageDetails, PhysicalDetail
class PackageDetailsSerializer(serializers.ModelSerializer):
is_expired = serializers.SerializerMethodField()
members_expiry_date = serializers.SerializerMethodField()
class Meta:
model = PackageDetails
exclude = ['id']
extra_fields = ['is_expired', 'members_expiry_date']
def get_is_expired(self, instance):
return instance.is_expired
def get_members_expiry_date(self, instance):
return instance.members_expiry_date
class PhysicalDetailSerializer(serializers.ModelSerializer):
class Meta:
model = PhysicalDetail
exclude = ['id']
class MemberSerializer(serializers.ModelSerializer):
physical_details = PhysicalDetailSerializer(many=True)
package_details = PackageDetailsSerializer(many=True)
class Meta:
model = Member
fields = '__all__'
extra_fields = ['physical_details', 'package_details']
def create(self, validated_data):
physical_detail_data = validated_data.pop("physical_details")
package_detail_data = validated_data.pop("package_details")
member = Member.objects.create(**validated_data)
PhysicalDetail.objects.create(member=member, **physical_detail_data)
PackageDetails.objects.create(member=member, **package_detail_data)
return member
views.py :
class MemberViewset(viewsets.ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
class PackageDetailViewset(viewsets.ModelViewSet):
queryset = PackageDetails.objects.all()
serializer_class = PackageDetailsSerializer
class PhysicalDetailViewset(viewsets.ModelViewSet):
queryset = PhysicalDetail.objects.all()
serializer_class = PhysicalDetailSerializer
In GET request it worked well.. but in POST request with the same json format it responses the following:
{
"physical_details": [
"This field is required."
],
"package_details": [
"This field is required."
]
}
I've provided the fields.. so why this happening..
You removed those from dict using pop()
The pop() method removes and returns an element from a dictionary having the given key.
Try using get() instead
The get() method returns the value for the specified key if the key is in the dictionary.

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

can not use different fields for each object in serializer django rest framework

I want to create a udemy like web app and using django rest framework for the backend and mysql as database.
i have a model named 'Lessons' that contains list of all lessons and one of the fields is 'video-link'. also i have another model names Purchases that have two ForeignKey fields :Users and Lessons. i want to show the Lessons to all users but for the download field i have to lookup the pair (User , Lesson) in Purchases and if He has the course i will show him the download field.
My View Set
class LessonsViewSet(viewsets.ModelViewSet):
queryset = models.Assignments.objects.all()
authentication_classes = (TokenAuthentication,)
def get_serializer_class(self):
if self.request.user.is_staff :
print(self.request.user)
return serializers.FullAccessLessonsSerializer
elif self.request.user.is_active:
return serializers.PartialAccessLessonsSerializer
print(self.request.user)
return serializers.BasicAccessLessonsSerializer
My Serializers
Full access for admins:
class FullAccessLessonsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description', 'dllink' )
Basic access for unauthenticated users:
class BasicAccessLessonsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description')
and partial access for students :
class PartialAccessAssignmentsSerializer(serializers.ModelSerializer):
"""A serializer for all Lessons"""
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields', None)
super(PartialAccessAssignmentsSerializer, self).__init__(*args,**kwargs)
print(self.fields.get('id'))
self.fields.pop('dllink')
class Meta:
model = models.Assignments
fields = ('id','title','description','dllink' )
I have done anything I could found but I can not figure it out. I either an get error or removing all dl links.
If you want dllink is None when user can't access.use this:
class FullAccessLessonsSerializer(serializers.ModelSerializer):
dllink = serializers.SerializerMethodField()
def get_prescription_accept(self, instance):
result = True # lookup the pair (User , Lesson) in Purchases
if result:
return instance.dllink
else:
return ''
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description', 'dllink' )
If you want pop dllink from data,use:
class FullAccessLessonsSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
data = super(FullAccessLessonsSerializer, self).to_representation(instance)
result = True # lookup the pair (User , Lesson) in Purchases
if not result:
data.pop('dllink')
return data
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description', 'dllink' )

Passing URL parameters to a nested serializer

I have two serializers, one of which is nested:
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
class AuthorSerializer(serializers.ModelSerializer):
papers = PaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
I want to get a list of Authors which shows only their published Papers (Boolean field exists on the model).
I would like to call the API like /api/v1/authors/?show_published_only=true.
After some digging around, I discovered that you can pass the context from the ViewSet to the Serializer:
views.py
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
filter_fields = (
'show_published_only',
)
def get_serializer_context(self):
return {'request': self.request}
Now, create a new serializer FilteredPaperSerializer which inherits from serializers.ListSerializer, then override the to_representation() method to filter the queryset:
serializers.py
class FilteredPaperSerializer(serializers.ListSerializer):
def to_representation(self, data):
# Get the parameter from the URL
show_published_only = self.context['request'].query_params['show_published_only']
data = data.filter(is_published=show_published_only)
return super(FilteredPaperSerializer, self).to_representation(data)
class AuthorSerializer(serializers.ModelSerializer):
papers = FilteredPaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
NB: Don't forget to convert the fetched URL parameter to a Boolean or relevant data type for your model, I neglected to do it in the write-up above.

Categories