How can I realize a APIView to get my requirement data? - python

I have two models:
class TypeModel(models.Model):
type = models.CharField(max_length=12)
class Amodel(models.Model):
name = models.CharField(max_length=12)
type = models.ForeignModel(to=TypeModel)
in_use = models.BooleanField(default=False)
the Amodel is the main model, it has a type field refer to TypeModel.
in the serializers.py:
class AmodelSerializer(ModelSerializer):
class Meta:
model = Amodel
fields = "__all__"
class TypeModelSerializer(ModelSerializer):
class Meta:
model = TypeModel
fields = "__all__"
My requirement is fetch the Amodel data like this, it should be divided by type, I want to get this type data:
[
{
"type": {
name: 'type1'
},
"count": {
"used": 12, # the in_use is `True`
"unused": 24
}
},
...
]
Calculate the used and unused count.
for amodel in amodel_qs:
if amodel.in_use: used_count+=1
else: unused_count +=1

Related

In DRF how do I serialize a related model(OneToOne) and display the data not in a list data type but single value instance?

The code is given below with the present output and expected output.
In the ProductPriceMapping table the ProductDetail table and PriceList are related with a OneToOne relation, but When the data for Price is fetched with the related_name argument there must be one value for one product, the data is being displayed is a list data type.
models.py
from django.db import models
class PriceList(models.Model):
priceCode = models.BigAutoField(primary_key= True)
maxRetailPrice= models.FloatField(max_length=20)
baseDiscount = models.FloatField(max_length=20, default=0)
seasonalDiscount = models.FloatField(max_length=20, default=0)
def __str__(self):
return '%s'% (self.maxRetailPrice)
class ProductDetail(models.Model):
productCode = models.BigAutoField(primary_key=True)
productName = models.CharField(max_length=100)
manufacturer = models.CharField(max_length=100)
def __str__(self):
return self.productName
class ProductPriceMapping(models.Model):
productPriceCode= models.BigAutoField(primary_key=True)
productCode= models.ForeignKey(ProductDetail,on_delete=models.CASCADE,related_name='price')
priceCode= models.OneToOneField(PriceList,on_delete=models.CASCADE)
def __str__(self):
return '%s' % (self.priceCode)
serializers.py
from rest_framework import serializers
from .models import CategoryDetail, EmployeeDetail, ProductCategoryMapping, ProductPriceMapping, SalaryDetail, ProductDetail, PriceList
class ProductPriceListSerializer(serializers.ModelSerializer):
class Meta:
model = PriceList
fields = ('priceCode','maxRetailPrice',
'baseDiscount', 'seasonalDiscount')
class ProductPriceMappingSerializer(serializers.ModelSerializer):
class Meta:
model= ProductPriceMapping
fields= ('productPriceCode','productCode', 'priceCode')
class ProductDetailsSerializer(serializers.ModelSerializer):
category= serializers.StringRelatedField(many= True, read_only= True)
price = serializers.StringRelatedField( many= True, read_only= True)
class Meta:
model = ProductDetail
fields = ('productCode', 'productName', 'manufacturer','category', 'price')
The result of API as it looks:
[
{
"productCode": 1,
"productName": "NeoChef",
"manufacturer": "LG",
"category": [
"1: Microwave Oven"
],
"price": [
"26000.0" ##expected the price value not be in a list
]
},
{
"productCode": 2,
"productName": "The Frame",
"manufacturer": "Samsung",
"category": [
"2: Television"
],
"price": [
"120000.0" ##expected the price value not be in a list
]
},
{
"productCode": 3,
"productName": "Galaxy S22+",
"manufacturer": "Samsung",
"category": [
"3: Smart Phone"
],
"price": [
"79000.0" ##expected the price value not be in a list
]
}
]
Expected result:
[
{
"productCode": 1,
"productName": "NeoChef",
"manufacturer": "LG",
"category": [
"1: Microwave Oven"
],
"price": "26000.0"
}
]```
In your case, the price field is not a one-to-one related field, you either need to change the productCode to OneToOneField or if you don't want to change the DB field, you can achieve the same result simply with SerializerMethodField. In the first case, removing many=True argument from the serializer field should help. In second case, SerializerMethodField will help you to make your custom representation, e.g.:
class ProductDetailsSerializer(serializers.ModelSerializer):
category= serializers.StringRelatedField(many=True, read_only=True)
price = serializers.SerializerMethodField()
class Meta:
model = ProductDetail
fields = ('productCode', 'productName', 'manufacturer','category', 'price')
def get_price(self, obj):
# If it's guaranteed that there will be only one related object, or retrieve the needed object depending on your demands
return str(obj.price.first())
One simple way is to use MethodField...
class ProductPriceMappingSerializer(serializers.ModelSerializer):
priceCode = serializers.SerializerMethodField()
class Meta:
model= ProductPriceMapping
fields= ('productPriceCode','productCode', 'priceCode')
#staticmethod
def get_priceCode(obj):
return obj.priceCode.maxRetailPrice # or any other fields that you like to show in your response
but if you want to show all of the priceList fields based on your other serializer you can do something like:
class ProductPriceMappingSerializer(serializers.ModelSerializer):
priceCode = ProductPriceListSerializer()
class Meta:
model= ProductPriceMapping
fields= ('productPriceCode','productCode', 'priceCode')
Thank you guys, I solved in this way:
models.py
class ProductPriceMapping(models.Model):
productPriceCode= models.BigAutoField(primary_key=True)
productCode= models.OneToOneField(ProductDetail,on_delete=models.CASCADE,related_name='price')
priceCode= models.ForeignKey(PriceList,on_delete=models.CASCADE)
def __str__(self):
return '%s' % (self.priceCode)
serializers.py
class ProductDetailsSerializer(serializers.ModelSerializer):
category= serializers.StringRelatedField(many= True, read_only= True)
price = serializers.StringRelatedField(read_only= True)
class Meta:
model = ProductDetail
fields = ('productCode', 'productName', 'manufacturer','category', 'price')

Custom Serializer and ViewSet for ManyToManyField in DRF [duplicate]

This question already has answers here:
Include intermediary (through model) in responses in Django Rest Framework
(4 answers)
Closed 3 years ago.
I have a M2M relationship between the Entity and EntityGroup and I want to save the corresponding entity index to the EntityGroup just like an entity array to database.
Since I used a custom through Model with additional field index inside, I need to serialize the index to the corresponding entity to the response, how should I implement that?
I'm new to django and django-rest-framework and it seems there are not similar M2M examples after a few google search. Here is my thought, a serializer could only serialize the only one model's fields with ForeignKey relationship, a viewset could have a custom model based on queryset which could merge the relationships for a few models. So I need to implement a more expandable viewset with a custom queryset inside?
Here is my code:
models.py
class Entity(models.Model):
uuid = models.CharField()
name = models.CharField()
class EntityGroup(models.Model):
name = models.CharField()
entities = models.ManyToManyField(Entity,
through='EntityGroupRelationship',
through_fields=('group', 'entity'),
related_name='groups'
)
class EntityGroupRelationship(models.Model):
entity = models.ForeignKey(Entity, on_delete=models.CASCADE)
group = models.ForeignKey(EntityGroup, on_delete=models.CASCADE)
index = models.PositiveIntegerField()
serializers.py
class EntitySerializer(serializers.ModelSerializer):
class Meta:
model = Entity
fields = '__all__'
class EntityGroupRelationshipSerializer(serializers.ModelSerializer):
class Meta:
model = EntityGroupRelationship
fields = '__all__'
class EntityGroupSerializer(serializers.ModelSerializer):
entities = EntitySerializer(many=True)
class Meta:
model = EntityGroup
fields = '__all__'
views.py
class EntityGroupViewSet(BaseModelViewSet):
queryset = EntityGroup.objects.all()
serializer_class = EntityGroupSerializer
class EntityGroupRelationshipViewSet(BaseModelViewSet):
queryset = EntityGroupRelationship.objects.all()
serializer_class = EntityGroupRelationshipSerializer
Current response
[
{
"id": 1,
"entities": [{
"id": 1,
"name": "",
}]
},
...
]
Expected respponse
[
{
"id": 1,
"entities": [
{
"index": 1,
"info": {
"id": 1,
"name": "",
}
}
]
},
...
]
You are using incorrect serializer for that. Use EntityGroupRelationshipSerializer instead of EntitySerializer. Also you need to pass correct fields
class EntitySerializer(serializers.ModelSerializer):
class Meta:
model = Entity
fields = '__all__'
class EntityGroupRelationshipSerializer(serializers.ModelSerializer):
entity = EntitySerializer()
class Meta:
model = EntityGroupRelationship
fields = ('index', 'entity')
class EntityGroupSerializer(serializers.ModelSerializer):
entities = EntityGroupRelationshipSerializer(many=True) # here should be EntityGroupRelationshipSerializer
class Meta:
model = EntityGroup
fields = '__all__'

Queryset in one to many relation

I am trying to get a Json of elements with their related elements
I had two tables, Service and Room. One service have many rooms. I would like to get the service where have room_id = x.
Models
class Service(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = True
db_table = 'Service'
class Room(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
service = models.ForeignKey(Service, models.DO_NOTHING, blank=True,
null=True)
class Meta:
managed = True
db_table = 'Room'
Serializer
class ServiceSerializer(serializers.ModelSerializer):
room_set = RoomSerializer(many=True, read_only=True)
class Meta:
model = Service
fields = ('name','room_set')
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = '__all__'
View
queryset = Service.objects.filter(room__id=1)
serializer = ServiceSerializer(queryset, many=True)
return JsonResponse(serializer.data, safe=False)
I expect a Json like this:
{
"name": "Hotel1",
"room_set": [
{
"id": 1,
"name": "Room1"
},
But I get this:
{
"name": "Hotel1",
"room_set": [
{
"id": 1,
"name": "Room1",
},
{
"id": 2,
"name": "Room2",
},
{
"id": 3,
"name": "Room3",
}
}
Is it possible to get a json like the one I'm expecting?
You can patch the set by adding a custom Prefetch object [Django-doc] with a filtered queryset, like:
from django.db.models import Prefetch
queryset = Service.objects.filter(
room__id=1
).prefetch_related(
Prefetch('room_set', queryset=Room.objects.filter(id=1), to_attr='room_set1')
)
serializer = ServiceSerializer(queryset, many=True)
return JsonResponse(serializer.data, safe=False)
and let the Serializer parse the new related manager:
class ServiceSerializer(serializers.ModelSerializer):
room_set = RoomSerializer(many=True, read_only=True, source='room_set1')
class Meta:
model = Service
fields = ('name','room_set1')
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = '__all__'
You can pass the room id via the serializer context and filter accordingly inside a SerializerMethodField()
class ServiceSerializer(serializers.ModelSerializer):
rooms = serializers.SerializerMethodField()
class Meta:
model = Service
fields = ('name','rooms')
get_rooms(self,service):
room_id = self.get_context('room')
if room_id:
queryset = service.rooms_set.filter(id=room_id)
return RoomSerializer(queryset,many=True).data
return RoomSerializer(service.rooms_set.all(),many=True).data
serializer = ServiceSerializer(queryset, many=True,context={'room':1})
return JsonResponse(serializer.data, safe=False)
This's how to do it via the serializer and it's highly customizable , Willem Van Onsem's answer is brief enough , but it also requires two queries the same as of mine.

Adding foreign key data to serializer?

I'm trying to use the Django Rest-Framework to produce some JSON that shows all the user's posts, but also shows the images for that post. Image is a foreign key to Post. Here are the models:
models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
status = models.CharField(max_length=200)
class Image(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
img = models.CharField(max_length=120)
views_count = models.IntegerField(default=0)
views.py
class GetPosts(ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
requested_user = get_requested_user(self)
return Post.objects.filter(user=requested_user).order_by('-created_at')
def get_requested_user(self):
filter_kwargs = {'username': self.kwargs['username']}
return get_object_or_404(User.objects.all(), **filter_kwargs)
serializers.py
class PostSerializer(serializers.ModelSerializer):
image_img = serializers.RelatedField(source='Image', read_only=True)
class Meta:
model = Post
fields = ('status', 'image_img ')
In the serializers.py, I'd like to show all of the fields for Image (img, views_count) What I get with my current code is this:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"status": "I am number 1"
}
]
}
Which contains the user's posts, but not the user's posts and each post's images. Note: Query url looks like this: /api/posts/user/
You should use Nested serializer here,
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = ('img',)
class PostSerializer(serializers.ModelSerializer):
image_img = ImageSerializer(source='image_set', many=True)
class Meta:
model = Post
fields = '__all__'
Hence the response will be like,
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"status": "I am number 1",
"image_img": [
{"img": "image_url"},
{"img": "image_url"},
....
]
}
]
}
How to display all field of model class in serializer?
From the doc,
You can also set the fields attribute to the special value '__all__' to indicate that all fields in the model should be used.
Reference
1. DRF- Nested Realtions
2. source argument
3. Specifying which fields to include

Django REST Framework nested serializer FK creation

I'm in trouble creating a bunch of related models using DRF nested serializers.
They are failing validation on the foreign key.
Models
class Employee(models.Model):
user = models.OneToOneField(User) # Django user
...
class Task(models.Model):
author = models.ForeignKey(Employee, related_name='tasks')
title = models.CharField(max_length=64)
...
class EmployeeTarget(models.Model):
employee = models.ForeignKey(Employee, null=False)
task = models.ForeignKey(Task, null=False, related_name='employee_targets')
...
Objective
Basically I have the Employees already created, and I want to create a Task and related EmployeeTarget in a single request, getting the request user as the author. JSON request example:
{
"title": "Lorem Ipsum",
"employee_targets": [
{ "employee": 10 },
{ "employee": 11 }]
}
/* or */
{
"title": "Lorem Ipsum",
"employee_targets": [10,11]
}
Serializers
class EmployeeSerializer(serializers.ModelSerializer):
name = serializers.CharField(source="user.get_full_name", read_only=True)
email = serializers.CharField(source="user.email", read_only=True)
class Meta:
model = Employee
class EmployeeTargetSerializer(serializers.ModelSerializer):
employee = EmployeeSerializer()
class Meta:
model = EmployeeTarget
class TaskSerializer(base.ModelSerializer):
employee_targets = EmployeeTargetSerializer(many=True, required=False)
class Meta:
model = Task
def create(self, validated_data):
employee_target_data = validated_data.pop('employee_targets')
task = Task.objects.create(**validated_data)
EmployeeTarget.objects.create(task=task, **employee_target_data)
return task
ViewSet
class TaskViewSet(ModelViewSet):
serializer_class = TaskSerializer
def get_queryset(self):
request_employee = self.request.user.employee
return Task.objects.filter(Q(author=request_employee) |
Q(employee_targets__employee=request_employee))
def perform_create(self, serializer):
serializer.save(author=self.request.user.employee)
Result
I'm getting 400 BAD REQUEST with the following error:
{
"employee_targets": [
{
"employee": {
"non_field_errors": ["Invalid data. Expected a dictionary, but got int."]
},
"task": ["This field is required."]
}
],
"author": ["This field is required."]
}
The employee error was expected, but I haven't figured out how to create them using only the ID.
The bigger problem here is the employee_targets failing validation at the task FK, before the enclosing TaskSerializer specify them at create method.
Can you try with this:
class EmployeeSerializer(serializers.ModelSerializer):
name = serializers.CharField()
email = serializers.CharField()
class Meta:
depth = 2
model = Employee

Categories

Resources