Formatting JSON in Serializer - Django REST - json

I am new to Django and Django-REST and I was tasked to create a serializer.
The output of my serializer looks like this:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "a",
"description": "a",
"price": "1.00",
"images": [
{
"image_addr": "http://127.0.0.1:8000/media/products/Screenshot_from_2018-05-16_15-07-34.png",
"product": 1
},
{
"image_addr": "http://127.0.0.1:8000/media/products/Screenshot_from_2018-05-16_16-42-55.png",
"product": 1
}
]
}
]
}
How can i tweak my serializer in a way that my output would look like this:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "a",
"description": "a",
"price": "1.00",
"images": [
"http://127.0.0.1:8000/media/products/Screenshot_from_2018-05-16_15-07-34.png",
"http://127.0.0.1:8000/media/products/Screenshot_from_2018-05-16_16-42-55.png"
]
}
]
}
The serializers that I am using are:
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ('image_addr', 'product')
and
class ProductSerializer(serializers.ModelSerializer):
images = ProductImageSerializer(many=True, read_only=True)
class Meta:
model = Product
fields = ('id', 'name', 'description', 'price','images')
My models used are:
class ProductImage(models.Model):
image_addr = models.FileField(upload_to='products/',null=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='images')
and
class Product(models.Model):
name = models.CharField(max_length=150)
price = models.DecimalField(max_digits=9, decimal_places=2)
product_count = models.IntegerField(blank=True, default=0, validators=[MinValueValidator(0)])
description = models.TextField()
category = models.ManyToManyField(Category)
objects = ProductManager()
My view used is:
class CategoryProductList(generics.ListAPIView):
serializer_class = ProductSerializer
def get_queryset(self):
queryset = Product.objects.filter(category__id=self.kwargs['pk'])
return queryset

You can use SerializerMethodField for your purpose. Change your ProductSerializer as below
class ProductSerializer(serializers.ModelSerializer):
images = serializers.SerializerMethodField(read_only=True, source='images')
def get_images(self, model):
return model.images.values_list('image_addr', flat=True)
class Meta:
model = Product
fields = ('id', 'name', 'description', 'price', 'images')

Related

Custom Response to display the data in list of JSON using django

models.py
class Project(models.Model):
project_id = models.CharField(max_length=50,default=uuid.uuid4, editable=False, unique=True, primary_key=True)
org = models.ForeignKey(Organisation, on_delete=models.CASCADE, related_name='org_project',null=True)
product = models.ManyToManyField(Product,related_name='product_project')
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='client_project')
project_name = models.CharField(unique=True,max_length=100)
project_code = models.CharField(max_length=20)
project_cost = models.IntegerField(null=True)
currency_type = models.CharField(max_length=100, choices=CURRENCY_CHOICES, default='Indian Rupee')
project_head = models.ForeignKey(User_Master, on_delete=models.CASCADE, related_name='project_head',null=True)
project_manager = models.ForeignKey(User_Master, on_delete=models.CASCADE, related_name='project_manager',null=True)
project_user = models.ManyToManyField(User_Master,related_name='project_user')
description = models.TextField(max_length=500)
start_date = models.DateField()
end_date = models.DateField()
techstack = models.ManyToManyField(Techstack,related_name='techstack_project')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.IntegerField(default=0, choices=STATUS_CHOICES)
views.py
from djmoney.settings import CURRENCY_CHOICES
class CurrencyList(APIView):
renderer_classes = (CustomRenderer,)
def get(self, request, *args, **kwargs):
return Response(CURRENCY_CHOICES, status=status.HTTP_200_OK)
I was Trying to get the Currency code list in a response so that the data can be sent to the frontend as a dropdown list.
When I Used the above django money package I am getting an response as
{
"status": "success",
"code": 200,
"data": [
[
"XUA",
"ADB Unit of Account"
],
[
"AFN",
"Afghan Afghani"
],
[
"AFA",
"Afghan Afghani (1927–2002)"
],
....
],
"message": null
}
But I need to Get the response as list of json inside the data not as the list of arrays,
{
"status": "success",
"code": 200,
"data": [
{
"shortname": "XUA",
"fullname": "ADB Unit of Account"
},
{
"shortname":"AFN",
"fullname":"Afghan Afghani"
},
{
"shortname":"AFA",
"fullname":"Afghan Afghani (1927–2002)"
},
....
],
"message": null
}
Is it possible to modify the response for this package? or is there any other way to achieve the end goal response by any other method?
You just need to adjust your response in the following way:
from djmoney.settings import CURRENCY_CHOICES
class CurrencyList(APIView):
renderer_classes = (CustomRenderer,)
def get(self, request, *args, **kwargs):
return Response(
[{'shortname': short, 'fullname': full} for short, full in CURRENCY_CHOICES],
status=status.HTTP_200_OK,
)
try this
from djmoney.settings import CURRENCY_CHOICES
class CurrencyList(APIView):
renderer_classes = (CustomRenderer,)
def get(self, request, *args, **kwargs):
return Response([dict(zip(['shortname', 'fullname'], c)) for c in CURRENCY_CHOICES], status=status.HTTP_200_OK)
python console
>>> data = [
... [
... "XUA",
... "ADB Unit of Account"
... ],
... [
... "AFN",
... "Afghan Afghani"
... ],
... [
... "AFA",
... "Afghan Afghani (1927–2002)"
... ]
]
>>>
>>> [dict(zip(['shortname', 'fullname'], c)) for c in data ]
[{'shortname': 'XUA', 'fullname': 'ADB Unit of Account'}, {'shortname': 'AFN', 'fullname': 'Afghan Afghani'}, {'shortname': 'AFA', 'fullname': 'Afghan Afghani (1927–2002)'}]

Django Rest Framework - How to get the json response in a way, so that it is easier to access in frontend?

Hey guys I have this serializer to get the list of pending users,
class UserInfo(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'username',]
class PendingRequestSerializer(serializers.ModelSerializer):
other_user = UserInfo(read_only=True)
class Meta:
model = PendingRequest
fields = ['other_user', ]
views.py
class PendingRequestListApiView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, *args, **kwargs):
user = PendingRequest.objects.filter(user=self.request.user)
serializer = PendingRequestSerializer(user, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I am getting the json response like this.
[
{
"other_user": {
"id": 8,
"username": "testuser5"
}
},
{
"other_user": {
"id": 4,
"username": "testuser2"
}
}
]
I want the json response to look like this instead.
"results": [
{
"id": 4,
"username": "testuser2",
},
{
"id": 8,
"username": "testuser5",
}
]
That is instead of having the user information in separate other_user, is it possible to combine it like it is in the second response?
Thanks
You can override to_representation() method of PendingRequestSerializer to return the other_user value
class PendingRequestSerializer(serializers.ModelSerializer):
class Meta:
model = PendingRequest
def to_representation(self, instance):
return UserInfo(instance.other_user).data

How to group by date in queryset

I need help with writing proper queryset in Django View. I have Post model with created_at (datetime field). I want to group it by date and return in a specific format.
models.py
class Post(TrackableDate):
title = models.CharField(max_length=255)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
views.py
class PostViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
The usual response looks like:
[
{
"id": 1,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
},
{
"id": 2,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
},
{
"id": 3,
"text": "ok",
"created_at": "2012-12-13T12:30:00"
}
]
How to group and return like that?
{
"2012-12-12": [
{
"id": 1,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
},
{
"id": 2,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
}
],
"2012-12-13": [
{
"id": 3,
"text": "ok",
"created_at": "2012-12-13T12:30:00"
}
]
}
I tried to do
Post.objects.extra(select={'created_at': 'date(created_at)'}).values('created_at').annotate(available=Count('created_at'))
But it returns
<QuerySet [{'created_at': '2020-07-04', 'available': 7}, {'created_at': '2020-07-09', 'available': 2}]>
This is a helper function, you can accomplish your results like this, it may be a better solution. This is the one that I came up with
def group_by_date(mydata):
new_data = {}
for element in mydata:
v = element['created_at'].split('T')[0]
if v not in new_data.keys():
new_data[v] = []
new_data[v].append(element)
return new_data
You can override the get function of your API and rework the output
and override return Response(group_by_date(serializer.data), HTTP_200_OK)

Change structure of JSON data for API POST request with Django

I have a Django REST API endpoint with this structure, that I need to post to an external API:
{
"body": [
"...",
"...",
"...",
],
"title": [
"...",
"...",
"...",
],
"id": [
"...",
"...",
"...",
]
}
The first item under 'body' goes with the first under 'title' and 'id', and so forth.
The problem I'm having is that the API in question expects JSON data with the following structure:
{
"texts": [
{
"body": "...",
"title": "...",
"id": "..."
},
{
"body": "...",
"title": "...",
"id": "..."
},
{
"body": "...",
"title": "...",
"id": "..."
},
],
"language": "EN",
}
And I can't figure out how have my endpoint mirror that structure, with the bodies, titles, and ids grouped together, those groupings nested under texts, and with the language parameter appended at the end.
The serializer I'm using in my views.py looks as follows:
class MyReasonsSerializer(serializers.Serializer):
body = serializers.SerializerMethodField()
title = serializers.SerializerMethodField()
id = serializers.SerializerMethodField()
def get_body(self, obj):
return obj.reasons.order_by('transaction_date').values_list('body', flat=True)
def get_title(self, obj):
return obj.reasons.order_by('transaction_date').values_list('title', flat=True)
def get_id(self, obj):
return obj.reasons.order_by('transaction_date').values_list('transaction_date', flat=True)
class ReasonsData(RetrieveAPIView):
queryset = Market.objects.all().prefetch_related('reasons')
authentication_classes = []
permission_classes = []
serializer_class = MyReasonsSerializer
Thanks in advance for any advice!
EDIT:
Here are the models:
class Market(models.Model):
title = models.CharField(max_length=50, default="")
current_price = models.DecimalField(max_digits=5, decimal_places=2, default=0.50)
description = models.TextField(default="")
...
language = models.CharField(max_length=2, default="EN")
def __str__(self):
return self.title[:50]
class Reason(models.Model):
user_id = models.ForeignKey('users.CustomUser',
on_delete=models.CASCADE,
related_name='user_reasons',
default=None)
market = models.ForeignKey(
Market,
on_delete=models.CASCADE,
related_name='reasons',
default=None)
...
valence = models.CharField(max_length=11, default="")
title = models.TextField(default="")
body = models.TextField(default="")
def __str__(self):
return str(self.title)
I would structure it like this... (its hard to know exactly without seeing the models and trying it out)
class ReasonSerializer(serializers.ModelSerializer):
class Meta:
model = Reason
fields = ("id", "body", "title")
class MarketSerializer(serializers.Serializer):
texts = ReasonSerializer(many=True, source="reasons")
language = serializers.CharField()
class Meta:
model = Market
class ReasonsData(RetrieveAPIView):
queryset = Market.objects.all()
authentication_classes = []
permission_classes = []
serializer_class = MarketSerializer
def get_queryset(self):
qs = super().get_queryset()
# filter queryset by valence if passed as a query param
# ?valence=something
valence = self.request.query_params.get("valence", None)
if valence is not None:
qs = qs.filter(reasons__valence=valence)
return qs

How to move a serializer.data into custom Instance (field) with Django Rest Framework

I am able to get the JSON result below using django-rest-framework.
{
"id": 1,
"fieldA": 1,
"fieldB": 100
},
{
"id": 2,
"fieldA": 2,
"fieldB": 101
},
{
"id": 3,
"fieldA": 1,
"fieldB": 101
}
I just want to change the structure to this.
{
"typeA":
{
"id": 1,
"fieldA": 1,
"fieldB": 100
}
},
{
"typeB":
{
"id": 2,
"fieldA": 2,
"fieldB": 100,
},
{
"id": 3,
"fieldA": 1,
"fieldB": 101,
}
}
typeA includes objects where fieldB == 100 and typeB includes objects where fieldB == 101.
I can handle the results using like Objects.filter() at get_query_set() in views.py. So you don't need to care about it.
The key of my question is how I can insert my customized field name and move the results to its child level. Currently, I guess the solution would be related to create() and update() functions from building a serializer. Please give any tips or hints.
Here's my model.py for more information.
class Results(models.Model):
fieldA = models.ForeignKey(Model)
fieldB = models.ForeignKey(EplTeams, null=True)
views.py
class ResultsView(generics.ListAPIView):
serializer_class = ResultSerializer
def get_queryset(self):
typeA = Results.objects.filter( fieldB=100 )
typeB = Results.objects.filter( fieldB=101 )
queryset = list( itertools.chain(typeA, typeB) )
return queryset
serializers.py
class ResultSerializer(serializers.ModelSerializer):
class Meta:
model = Results
You can do this by using ListSerializer
In your serializers.py:
class CustomListSerializer(serializers.ListSerializer):
def to_representation(self, data):
resdata = []
data1 = data.filter(fieldB=100)
data2 = data.filter(fieldB=101)
resdata.append({'typeA': super(CustomListSerializer, self).to_representation(data1)})
resdata.append({'typeB': super(CustomListSerializer, self).to_representation(data2)})
return resdata
class ResultSerializer(serializers.ModelSerializer):
class Meta:
model = Results
list_serializer_class = CustomListSerializer
Learn more about ListSerializer here