Regroup duplicate values into single key value array in Django Rest Framework? - django-rest-framework

I am using DRF to create an api:
My Models are:
from django.db import models
class Product(models.Model):
productId = models.CharField(max_length=120, primary_key=True)
productCategory = models.CharField(max_length=120)
name = models.CharField(max_length=120)
insertTime = models.DateField()
def __str__(self):
return self.productId
class LendingRate(models.Model):
productId = models.ForeignKey(Product, related_name="lendingRates", on_delete=models.CASCADE)
rate = models.CharField(max_length=120)
tiersName = models.CharField(max_length=120)
tiersMinimumValue = models.CharField(max_length=120)
tiersMaximumValue = models.CharField(max_length=120)
insertTime = models.DateField()
def __str__(self):
return self.rate
My Serializers are:
from rest_framework import serializers
from .models import Product
from .models import LendingRate
class LendingRateSerializer(serializers.ModelSerializer):
class Meta:
model = LendingRate
fields = ('rate', 'tiersName', 'tiersMinimumValue', 'tiersMaximumValue')
class ProductSerializer(serializers.ModelSerializer):
lendingRates = LendingRateSerializer(many=True)
class Meta:
model = Product
fields = ('productId','productCategory', 'name', 'lendingRates')
This would provide me a response like this:
"data": {
"productId": "62678a1c-daa8-491c-941e-63745620338f",
"productCategory": "RESIDENTIAL_MORTGAGES",
"name": "Fixed Rate Home Loan - 1 Year",
"lendingRates": [
{
"rate": "0.0529",
"tiersName": "LVR",
"tiersMinimumValue": "0.2934",
"tiersMaximumValue": "0.3932"
},
{
"rate": "0.0529",
"tiersName": "LVR",
"tiersMinimumValue": "0.0863",
"tiersMaximumValue": "0.0974"
},
{
"rate": "0.0619",
"tiersName": "LVR",
"tiersMinimumValue": "0.0437",
"tiersMaximumValue": "0.0621"
},
{
"rate": "0.0619",
"tiersName": "LVR",
"tiersMinimumValue": "0.0697",
"tiersMaximumValue": "0.0775"
},
{
"rate": "0.0348",
"tiersName": "LVR",
"tiersMinimumValue": "0.0224",
"tiersMaximumValue": "0.0443"
}
]
}
I am not able to group the rate with same value into an array.
As seen in the api response,I want to group the response with same rate into single array of objects.
For example like this:
desired api response
Here is my views.py
#api_view()
def productDetail(request, productId):
try:
product = Product.objects.get(productId=productId)
except Product.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
productSerializer = ProductSerializer(product, context={'request': request}).data
(don't know how to proceed from here)
.........
....
.
.
.
return Response()
I highly appreciate any help on this.
Thank you.

Related

How to get multiple records in the main model based on an array from a nested serializer?

I have next code:
models.py
class tbox(models.Model):
name = models.CharField("box", max_length=15)
status = models.CharField("status box", max_length=50)
class tbox_move(models.Model):
tbox = models.ForeignKey('api.tbox',related_name='tbox_moves', on_delete=models.DO_NOTHING)
discription = models.TextField(null=True, blank=True)
def __str__(self):
return '%d: %s' % (self.order, self.title)
serializer.py
from rest_framework import serializers
from .models import tbox, tbox_move
class tboxSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False, write_only=False)
class Meta:
model = tbox
fields = ['id', 'name', 'status']
def update(self, instance, validated_data):
print("Updade in serializer TBOX")
class tbox_moveSerializer(serializers.ModelSerializer):
tbox_moves = tboxSerializer(many=True)
class Meta:
model = tbox_move
fields = ['tbox_moves', 'discription']
def create(self, validated_data):
tboxs_data = validated_data.pop('tbox_moves')
for tbox_data in tboxs_data:
tbox_id = tbox_data.get("id", None)
if tbox_id is None:
print("requare BOX ID")
id_tbox = tbox.objects.filter(id=tbox_id).update(**tbox_data)
tbox_m = tbox_move.objects.create(tbox=tbox_id, **validated_data)
return tbox_m
I send next request POST json:
{
"tbox_moves" : [
{"id" : 3, "id_test" : 1, "name" : "name_box1", "status": "block"},
{"id" : 1, "id_test" : 1, "name" : "name_box2", "status": "block"}
],
"description": "collective data"
}
I want to get the following result.
Create multiple records in the model equal to the number in the nested serializer
Example tbox_move model:
ID
discription
tbox_id
10
collective data
3
11
collective data
1
I want update status in the nested model tbox from enable to block
How this create?

How can I add a field to a model serializer that has a reverse relationship with another model

I have two models. Fiction and Review model. They are the following:
class Fiction(models.Model):
"""
Model that encopasses a Movie, TV Series, book or similar
"""
MOVIE = 1
TV_SERIES = 2
BOOK = 3
PODCAST = 4
TYPE = (
(MOVIE, 'Movie'),
(TV_SERIES, 'TV-Series'),
(BOOK, 'Book'),
(PODCAST, 'Podcast')
)
title = models.CharField(max_length=50)
description = models.CharField(max_length=200)
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
platform = models.ForeignKey(
StreamPlatform,
on_delete=models.SET_NULL,
related_name='fictions',
null = True
)
type = models.PositiveSmallIntegerField(
choices = TYPE,
default = MOVIE
)
def __str__(self):
return self.title
and
class Review(models.Model):
"""
model for fiction reviews from users
"""
rating = models.PositiveSmallIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
fiction = models.ForeignKey(Fiction, on_delete=models.CASCADE, related_name="reviews")
description = models.CharField(max_length=200, null = True, blank =True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.rating) + " | " + str(self.fiction)
class Meta:
ordering = ['-created']
and also two serializers
for fiction
class FictionSerializer(serializers.ModelSerializer):
"""
serializer for Movie model
"""
class Meta:
model = Fiction
fields = "__all__"
and for review
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ['rating', 'fiction', 'description']
I want to be able to display the rating of the review inside the fiction serializers. I tried something like:
rating = serializers.ReadOnlyField(source='reviews.rating')
but it didnt work. Anyone has an idea?
Since you added reviews as a related name, you can use that.
Here is a working example for you. (I've created a small project for this, so this definitely works)
class ReviewRatingSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ('rating', )
class FictionSerializer(serializers.ModelSerializer):
"""
serializer for Movie model
"""
reviews = ReviewRatingSerializer(many=True)
class Meta:
model = Fiction
fields = "__all__"
This might cause lots of database queries if you want to return lots of Fiction items at once.
To fix that, you should use prefetch_related in your views.py
Here is a simple example for a list view.
class GetFictionMovies(ListAPIView):
pagination_class = None
serializer_class = FictionSerializer
def get_queryset(self):
queryset = Fiction.objects.all().prefetch_related('reviews')
return queryset
Output will be similar to this.
[
{
"id": 1,
"reviews": [
{
"rating": 3
},
{
"rating": 4
}
],
"title": "Starwars",
"description": "asdasd",
"active": true,
"created": "2021-06-27T16:28:55.521748Z",
"type": 1
},
{
"id": 2,
"reviews": [
{
"rating": 5
},
{
"rating": 2
}
],
"title": "LOTR",
"description": "asdasd",
"active": true,
"created": "2021-06-27T16:29:03.227639Z",
"type": 1
},
{
"id": 3,
"reviews": [
{
"rating": 4
},
{
"rating": 3
}
],
"title": "GODFATHER",
"description": "asdasd",
"active": true,
"created": "2021-06-27T16:34:45.171444Z",
"type": 1
}
]
My advice for you is to always check for number of queries made to the db and try to avoid duplicate calls to the db.

Nested serializer doesn't pick up correct ID

There are two models, they are defined this way:
class ShoppingList(models.Model):
id = models.CharField(max_length=40, primary_key=True)
name = models.CharField(max_length=40)
session_id = models.CharField(max_length=40)
config_file = models.FileField(upload_to=upload_config_file)
def __str__(self):
return self.id
class FetchedData(models.Model):
model_id = models.CharField(max_length=40)
config_id = models.ForeignKey(BillOfMaterial, on_delete=models.CASCADE, default=0)
config_name = models.CharField(max_length=40)
def __str__(self):
return self.model_id
And serialized like this:
class FetchedDataSerializer(serializers.ModelSerializer):
file_fields = serializers.SerializerMethodField()
class Meta:
model = FetchedData
fields = ('model_id', 'config_id', 'config_name', 'file_fields')
def get_file_fields(self, obj):
print(obj)
# queryset = ShoppingList.objects.filter(config_file = obj) ## (1)
queryset = BillOfMaterial.objects.all() ## (2)
return [ShoppingListSerializer(cf).data for cf in queryset]
I was advised* to implement the solution marked as (1) in the serializer above, but when it's on, I get responses with an empty array, for example:
[
{
"model_id": "6553",
"config_id": "2322",
"config_name": "Config No. 1",
"file_fields": []
}
]
Meanwhile, with option (2) turned on and option (1) commented out, I get all the instances displayed:
[
{
"model_id": "6553",
"config_id": "2322",
"config_name": "Config No. 1",
"file_fields": [
{
"id": "2322",
"name": "First Example",
"session_id": "9883",
"config_file": "/uploads/2322/eq-example_7DQDsJ4.json"
},
{
"id": "4544",
"name": "Another Example",
"session_id": "4376",
"config_file": "/uploads/4544/d-jay12.json"
}
]
}
]
The print(obj) method always gives a model_id value. And it should output file_fields.id, I guess.
How should I re-build this piece of code to be able to display only the file_field with id matching config_id of the parent?
*This is a follow-up of an issue described here: TypeError: 'FieldFile' object is not callable
In FetchedData model I added this method:
def config_link(self):
return self.config_id.config_file
(it binds config_file from ShoppingList model).
FetchedDataSerializer should then look like this:
class FetchedDataSerializer(serializers.ModelSerializer):
file_link = serializers.SerializerMethodField()
class Meta:
model = FetchedData
fields = ('model_id', 'config_id', 'config_name', 'file_link')
def get_file_link(self, obj):
return obj.config_link()

How to delete any filed in ViewSet

class DepartSerializer(serializers.HyperlinkedModelSerializer):
attrs = AttrSerializer(source="depattr", many=True)
people = PeopleSerializer(source='perdepart', many=True)
class Meta:
model = Departs
fields = ('url', 'name', 'describe', 'pinyin', 'attrs', 'people')
class DepartsViewSet(viewsets.ReadOnlyModelViewSet):
"""
i want delete people field in List , and Retain people fieled in retrieve.
"""
queryset = Departs.objects.filter(disabled=False).order_by('-uptime')
serializer_class = DepartSerializer
1.I want the result like this:
2.get /depart
[
{"name":"depart1","id":1},
{"name":"depart2","id":2},
]
3.get /depart/1
{
"name": "depart1",
"id": 1,
"people": [{
"id": 1,
"name": "per1"
},
{
"id": 2,
"name": "per2"
}
]
}
You can use different serializers in your viewset depending on the action by overriding the viewset's get_serializer_class:
def get_serializer_class(self):
if self.action == 'retrieve':
return DepartSerializerWithoutPeople
return DepartSerializer
retrieve is the action called by get /depart/1/. And then you can define your DepartSerializerWithoutPeople like this:
class DepartSerializerWithoutPeople(serializers.HyperlinkedModelSerializer):
attrs = AttrSerializer(source="depattr", many=True)
class Meta:
model = Departs
fields = ('url', 'name', 'describe', 'pinyin', 'attrs',)

How to write an API that allows PUT value in the url itself

I have the below classes:
Models.py
class User(models.Model):
name = models.CharField(mex_length=50)
email = models.EmailField(max_length=100)
department = models.CharField(max_length=50)
def __str__(self):
return self.name
Views.py
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
#action(methods=['PUT'], detail=True)
def update_department(self, request, *args, **kwargs)
obj = self.get_object()
??
class DepartmentSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('department', )
Now I have 2 users in my database:
{
"id": 1,
"name": "abcd",
"email": "abcd#gmail.com",
"department": "accounts"
}
{
"id": 2,
"name": "efgh",
"email": "efgh#gmail.com",
"department": "HR"
}
I want to update the department for user 2 to "accounts" with an api as below where I pass the value to be update with the api url itself:
PUT "/api/user/2/update_department/accounts"
How can this be achieved with DRF?
Urls.py
from django.conf.urls import url
from django.conf.urls import include
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'user', views.UserViewSet, base_name='user_view')
slashless_router = routers.DefaultRouter(trailing_slash=False)
slashless_router.registry = router.registry[:]
urlpatterns = [
url(r'', include(router.urls)),
url(r'', include(slashless_router.urls)),
]

Resources