I have 3 different models. I need to build a custom serializer with these 3 models. I tried using some code, but it didn't reach what I expected.
There is structure, but I was not able to get the data I was expecting
# Serializer
This my Serializer and ModelOne, ModelTwo, ModelThree these are my models.
class ModelOneSerializer(serializers.ModelSerializer):
class Meta:
model = ModelOne
fields = ['id', 'obj_1', 'obj_2']
class ModelTwoSerializer(serializers.ModelSerializer):
class Meta:
model = ModelTwo
fields = ['id', 'obj_1', 'obj_2']
class ModelThreeSerializer(WritableNestedModelSerializer):
class Meta:
model = ModelThree
fields = ['id', 'obj_1', 'obj_2']
class CustomSerializer(serializers.Serializer):
model_1 = ModelOneSerializer(many=True)
model_2 = ModelTwoSerializer(many=True)
model_3 = ModelThreeSerializer(many=True)
# View
class CustomView(APIView):
def get(self, request, *args, **kwargs):
serializer = CustomSerializer(context={'request': request})
return Response({'response': 'ok', 'result': serializer.data})
# Expected Output
{
"response": "ok",
"result": {
"model_1": [
{
"id":"1",
"obj_1":"test",
"obj_2":"test",
"obj_3":"test"
},
{
"id":"1",
"obj_1":"test",
"obj_2":"test",
"obj_3":"test"
}
],
"model_2": [
{
"id":"1",
"obj_1":"test",
"obj_2":"test",
"obj_3":"test"
}
],
"model_3": [
{
"id":"1",
"obj_1":"test",
"obj_2":"test",
"obj_3":"test"
}
]
}
}
The results may have multiple data in the model_2 and model_3 as in the model_1 structure
You didn't pass what to serialize for all the model serializers, so CustomSerializer doesn't have anything to serialize against.
You can pass the querysets of the models to CustomSerializer:
class CustomView(APIView):
def get(self, request, *args, **kwargs):
model_one_qset = ModelOne.objects.all()
model_two_qset = ModelTwo.objects.all()
model_three_qset = ModelThree.objects.all()
qsets_map = {
'model_1': model_one_qset,
'model_2': model_two_qset,
'model_3': model_three_qset,
}
serializer = CustomSerializer(qsets_map, context={'request': request})
return Response({'response': 'ok', 'result': serializer.data})
Related
I am trying to create a relationship between databases
result : =>
[
{
"id":1,
"title":"mobile",
"category_two":[
{
"id":3,
"title":"brand"
},
{
"id":4,
"title":"memory"
}
]
}
]
and i expect : =>
[
{
"id":1,
"title":"mobile",
"category_two":[
{
"id":3,
"title":"brand",
"category_three":[
{
"id":1,
"title":"samsung"
},
{
"id":2,
"title":"apple"
}
]
},
{
"id":4,
"title":"memory",
"category_three":[
{
"id":1,
"title":"32gb"
},
{
"id":2,
"title":"64gb"
}
]
}
]
}
]
// views
class get_Category(APIView):
def get(self, request):
category = CategoryOne.objects.all()
serializer = CategoryTwoSerializer(category, many=True)
return Response(serializer.data)
//serializers
class CategoryOneSerializer(serializers.ModelSerializer):
class Meta:
model = CategoryOne
fields = '__all__'
class CategoryTwoSerializer(serializers.ModelSerializer):
category_two= CategoryOneSerializer(many=True,read_only=True)
class Meta:
model = CategoryTwo
fields = '__all__'
depth=5
class CategoryThreeSerializer(serializers.ModelSerializer):
category_three = CategoryTwoSerializer(many=True,read_only=True)
class Meta:
model = CategoryThree
fields = '__all__'
depth=5
// models
class CategoryOne(models.Model):
title = models.CharField(max_length=225)
def __str__(self):
return self.title
class CategoryTwo(models.Model):
title = models.CharField(max_length=255)
categoryone = models.ForeignKey(CategoryOne,related_name='category_two',on_delete=models.SET_NULL,null=True)
def __str__(self):
return self.title
class CategoryThree(models.Model):
title = models.CharField(max_length=255)
categorytwo = models.ForeignKey(CategoryTwo,related_name='category_three',on_delete=models.SET_NULL,null=True)
def __str__(self):
return self.title
Try
class CategoryOneSerializer(serializers.ModelSerializer):
category_two= CategoryTwoSerializer(many=True,read_only=True)
class Meta:
model = CategoryOne
fields = '__all__'
class CategoryTwoSerializer(serializers.ModelSerializer):
category_three = CategoryThreeSerializer(many=True,read_only=True)
class Meta:
model = CategoryTwo
fields = '__all__'
class CategoryThreeSerializer(serializers.ModelSerializer):
class Meta:
model = CategoryThree
fields = '__all__'
// views
class get_Category(APIView):
def get(self, request):
category = CategoryOne.objects.prefetch_related("category_two", "category_two__category_three")
serializer = CategoryOneSerializer(category, many=True)
return Response(serializer.data)
If you want category_two rows related to a specific category_one to be serialized with it, you essentially want to add a non existing data to CategoryOne.
Since means you need two add it to the serializer and specify how to serialize it.
Here you can access this data by using a related_name, therefore adding a field with the proper related_name will suffice for django to find the data you want.
The same wan be done the serializer CategoryThree tied the a specific CategoryTwo row.
Howerver doing this will produce a LOT of SQL queries since each time my_category_on.category_two.all() is called by the serializer, an sql query is needed.
The prefetch_related() is here to solve the issue
Can someone advise me on how to deal with how to retrieve data such as this example
JSON:
[
{
"deviceid": 4,
"devicename": "###",
"device_measurment": {
"deviceid": 4,
"measurement": "31.8",
}
},
]
How is it right now:
[
{
"deviceid": 4,
"devicename": "###",
},
{
"deviceid": 4,
"measurement": "31.8",
},
]
I understand the problem and why I am getting such a response, but I cannot handle this problem via the serializer. at the moment, I have the following
Views
from itertools import chain
class RetrieveSpecificDevicesView(APIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = SpecificDeviceSerializer
def post(self, request):
if self.request.method == 'POST':
serializer = SpecificDeviceSerializer(data=request.data)
if serializer.is_valid():
obj1= Devices.objects.using('###').filter(deviceid=serializer.data.get("deviceid"))
obj2= Measurements.objects.using('###').filter(deviceid=serializer.data.get("deviceid"))
dats= chain(obj1, obj2)
data = serializers.serialize('json', dats)
return HttpResponse(data, content_type="application/json")
return JsonResponse(serializer.erorr, status=status.HTTP_201_CREATED)
Serializer
class SpecificDeviceSerializer(serializers.Serializer):
deviceid = serializers.CharField(required=True)
class MeasurmentsSerializer(serializers.ModelSerializer):
class Meta:
model = Measurements
fields = ('measurementid','measurement')
Models
enter image description here
Update:
I am trying to implement via serializer but something is going wrong:
Note: By wrong I mean => *Got AttributeError when attempting to get a value for field devicesname on serializer SpecificDeviceSerializer*.
Views
class RetrieveSpecificDevicesView(APIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = SpecificcDeviceSerializer
def post(self, request):
if self.request.method == 'POST':
data = self.serializer_class.get_device(self, obj=request.data)
user = SpecificDeviceSerializer(data)
return HttpResponse(user)
Serializers
class SpecificDeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Devices
fields = ( 'deviceid', 'devicename')
class SpecificcDeviceSerializer(serializers.ModelSerializer):
def get_device(self, obj):
device = Devices.objects.using('###').filter(deviceid=obj.get("deviceid"))
return device
class Meta:
model = Devices
fields = ( 'deviceid', 'devicename')
Solution:
Serializer
class SpecificDeviceSerializer(serializers.ModelSerializer):
measurments = serializers.SerializerMethodField()
def get_measurments(self, obj):
device_measurements = obj.measurment_details.last()
serializer = MeasurmentsSerializer(device_measurements)
return serializer.data
class Meta:
model = Devices
fields = ('devicename','measurments')
Views
class RetrieveSpecificDevicesView(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
serializer_class = SpecificDeviceSerializer
def get_queryset(self):
return Devices.objects.using('###').filter(deviceid=self.request.data.get("deviceid"))
JSON:
[
{
"devicename": "RTMU 3068",
"measurments": {
"measurementid": 2980465,
"measurement": "25.6"
}
}
]
Documentation: Best for this case
Parent id or foreign key is not returning from child. I am trying drf-nested-routers example.
models.py
class Client(models.Model):
name = models.CharField(max_length=255)
class MailDrop(models.Model):
title = models.CharField(max_length=255)
client_id = models.ForeignKey(Client, on_delete=models.CASCADE)
serializers.py
class ClientSerializer(HyperlinkedModelSerializer):
class Meta:
model = Client
fields = ['id', 'name']
class MailDropSerializer(NestedHyperlinkedModelSerializer):
parent_lookup_kwargs = {
'client_pk': 'client_id',
}
class Meta:
model = MailDrop
fields = ['id', 'title']
views.py
class ClientViewSet(viewsets.ViewSet):
serializer_class = ClientSerializer
def list(self, request):
queryset = Client.objects.filter()
serializer = ClientSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = Client.objects.filter()
client = get_object_or_404(queryset, pk=pk)
serializer = ClientSerializer(client)
return Response(serializer.data)
class MailDropViewSet(viewsets.ViewSet):
serializer_class = MailDropSerializer
def list(self, request, client_pk=None):
queryset = MailDrop.objects.filter(client_id=client_pk)
serializer = MailDropSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None, client_pk=None):
queryset = MailDrop.objects.filter(pk=pk, client_id=client_pk)
maildrop = get_object_or_404(queryset, pk=pk)
serializer = MailDropSerializer(maildrop)
return Response(serializer.data)
If I add fields = ['id', 'title', 'client_id'] in the MailDropSerializer, it throws the following error:
AssertionError: `NestedHyperlinkedRelatedField` requires the request in the serializer context. Add `con
text={'request': request}` when instantiating the serializer.
If I add the request in the serializer context, the output is as follows:
url: http://127.0.0.1:8000/clients/4/maildrops/
[
{
"id": 3,
"title": "Madison Mosley",
"client_id": "http://127.0.0.1:8000/clients/4/"
},
{
"id": 4,
"title": "Louis Chen",
"client_id": "http://127.0.0.1:8000/clients/4/"
}
]
I added the serializer context as follows:
def list(self, request, client_pk=None):
queryset = MailDrop.objects.filter(client_id=client_pk)
serializer_context = {
'request': request
}
serializer = MailDropSerializer(
queryset, many=True, context=serializer_context)
return Response(serializer.data)
I am trying to get something as follows:
[
{
"id": 3,
"title": "Madison Mosley",
"client_id": 4
},
{
"id": 4,
"title": "Louis Chen",
"client_id": 4
}
]
Just solved:
class MailDropSerializer(NestedHyperlinkedModelSerializer):
parent_lookup_kwargs = {
'client_pk': 'client_id',
}
client_id = IntegerField(source='client_id.id')
class Meta:
model = MailDrop
fields = ['id', 'title', 'client_id']
I'm Trying to add the sum of multiple objects to a DRF response. For example, right now the response works with just listing the objects:
[
{
"id": "47d0deaa5c8c",
"amount": "25.00"
},
{
"id": "29787731",
"amount": "25.00"
}
]
But what I want is to be able to sum the amount attribute of those objects together and then include that in the response, so that it would look like this:
{
"sum":"50.00",
"objects":[
{
"id":"47d0deaa5c8c",
"amount":"25.00"
},
{
"id":"29787731",
"amount":"25.00"
}
]
}
Here's my current APIView:
class TransactionsList(GenericAPIView):
"""
Retrieve list of transactions
"""
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request):
"""List Transactions"""
transaction = Transaction.objects.all()
serializer = TransactionSerializer(transaction, many=True)
return Response(serializer.data)
And Serializer:
class TransactionSerializer(serializers.ModelSerializer):
class Meta:
model = Transaction
fields = ('id', 'amount')
How could I efficiently go about adding the sum field into the response?
change
return Response(serializer.data)
to
from django.db.models import Sum
all_sum = transaction.aggregate(Sum('amount'))['amount__sum']
return Response({'sum': all_sum if all_sum else 0 , 'objects': serializer.data})
Since the amount field seems string type. So, we've to convert to int before summing them.
class TransactionsList(GenericAPIView):
"""
Retrieve list of transactions
"""
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request):
"""List Transactions"""
transaction = Transaction.objects.all()
serializer = TransactionSerializer(transaction, many=True)
return_data = {"sum": str(sum([lambda items: int(items['amount'])])), "objects": serializer.data}
return Response(return_data)
If the amount field is int type, then you don't want to add do the type casting. If so, use this,
return_data = {"sum": sum([lambda items: items['amount']]), "objects": serializer.data}
I have the next view on my API
class CapsuleViewSet(viewsets.ModelViewSet):
queryset = Capsule.objects.all()
serializer_class = CapsuleSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
#action(detail=True)
def modules(self, request, pk=None):
capsule = self.get_object()
capsule_modules = Module.objects.filter(capsule=capsule)
serializer = ModuleSerializer(capsule_modules, many=True)
return Response(serializer.data)
When i try to get all the modules related to a capsule, the URL of the imageField of a module is incomplete.
When i call http://0.0.0.0:8000/api/capsules/1/ it returns:
{
"capsuleID": 1,
"capsuleName": "sdfads",
"capusuleDetails": "asdf",
"capsuleImageURL": "http://0.0.0.0:8000/media/capsulas/logocitbm.png",
"userStars": 0,
"pallete": {
"palleteID": 1,
"palleteName": "Default smartraining",
"colors": [
{
"colorID": 1,
"colorName": "A1",
"colorCode": "#sdfs"
},
{
"colorID": 2,
"colorName": "A2",
"colorCode": "#dsfksdoif"
},
{
"colorID": 3,
"colorName": "A3",
"colorCode": "#sdjfjgioj"
}
]
}
}
but when i call http://0.0.0.0:8000/api/capsules/1/modules/ i get:
[
{
"moduleID": 1,
"moduleName": "sdfa",
"moduleDetails": "asÃ",
"moduleImageURL": "/media/modulos/9_-_4._Detalle_C%C3%A1psula.png",
"userScore": 0
}
]
the moduleImageURL is incomplete, why?
these are my serializers:
class CapsuleSerializer(serializers.ModelSerializer):
pallete = PalleteSerializer(read_only=True)
class Meta:
model = Capsule
fields = ('capsuleID', 'capsuleName', 'capusuleDetails', 'capsuleImageURL', 'userStars', 'pallete')
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = Module
fields = ('moduleID', 'moduleName', 'moduleDetails', 'moduleImageURL', 'userScore')
After some research i find that i need pass the context to the ModelSerializer like this:
serializer = ModuleSerializer(capsule_modules, many=True, context={'request': request})