Is there a way to filter multiple nested serializers?
I have a Student serializer that displays all of the students courses. The courses display all the homework and their scores. They way I want to design the backend is that every student will receive the same homework outline but only the scores change; so to reduce duplicate data I create the scores table that points to the homework and the student.
However, when I do a GET request it gets all of the students scores for that homework instance. Is there a way to filter it to only get a particular students score? I simplified my models for this example but the problem is essentially the same.
In my Response, you can see "score":1 and "score":2, these belong to two different students but I want to get the specific score that relates to the a particular Student.
Models
class Student(models.Model):
firstName = models.CharField(max_length=20)
age = models.IntegerField(default=18)
def __str__(self):
return self.firstName
class Course(models.Model):
courseName = models.CharField(max_length=20)
courseYear = models.IntegerField(default=2021)
student = models.ManyToManyField(Student, related_name='courses')
def __str__(self):
return self.courseName + " " + str(self.courseYear)
class Homework(models.Model):
hwName = models.CharField(max_length=20)
hwPossScore = models.IntegerField(default=100)
course = models.ForeignKey(
Course, related_name='homeworks', on_delete=models.CASCADE, null=True, blank=True)
students = models.ManyToManyField(Student)
def __str__(self):
return self.hwName
class Score(models.Model):
student = models.ForeignKey(
Student, related_name='studentScore', on_delete=models.CASCADE, null=True, blank=True)
homework = models.ForeignKey(
Homework, related_name='studentScore', on_delete=models.CASCADE, null=True, blank=True)
score = models.IntegerField(default=0)
def __str__(self):
return self.student.firstName + " " + str(self.score)
Serializers
class ScoreSerializer(serializers.ModelSerializer):
class Meta:
model = Score
fields = ['score', ]
class HomeworkSerializer(serializers.ModelSerializer):
studentScore = ScoreSerializer(many=True)
class Meta:
model = Homework
fields = ['hwName', 'hwPossScore', 'studentScore', ]
class CourseSerializer(serializers.ModelSerializer):
homeworks = HomeworkSerializer(many=True)
class Meta:
model = Course
fields = "__all__"
class StudentSerializer(serializers.ModelSerializer):
courses = CourseSerializer(many=True)
class Meta:
model = Student
fields = "__all__"
Views
class StudentView(APIView):
permission_classes = [permissions.IsAuthenticated, ]
def get_object(self, request):
try:
requestToken = request.META['HTTP_AUTHORIZATION'].split(' ')[1]
userObj = Token.objects.get(key=requestToken).user
studentObj = Student.objects.filter(user=userObj)[0]
# If requester token does not match user return permission denied
if(self.request.user != userObj):
raise PermissionDenied()
return studentObj
except Student.DoesNotExist:
raise Http404
def get(self, request, format=None):
# Create a field variable for all Board objects that count the number of topics for each board
student = self.get_object(request)
serializer = StudentSerializer(student)
return Response(serializer.data)
Response
{
"id": 1,
"courses": [
{
"id": 1,
"homeworks": [
{
"hwName": "HW1",
"hwPossScore": 100,
"studentScore": [
{
"score": 1
},
{
"score": 2
}
]
}
],
"courseName": "MATH 101",
"courseYear": 2021,
"student": [
1
]
}
],
"firstName": "Red",
"age": 18
}
I think you can use ScoreSerializer instead StudentSerializer. In ScoreSerializer, that class inheritance ModelSerializer you can use field = 'all', because in ScoreModel has student, score, and homeworks field.
Related
models.py
class Product(models.Model):
product_id = models.AutoField(unique=True, primary_key=True)
product_name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "product_master"
def __str__(self):
return self.product_name
class Organisation(models.Model):
"""
Organisation model
"""
org_id = models.AutoField(unique=True, primary_key=True)
org_name = models.CharField(max_length=100)
org_code = models.CharField(max_length=20)
org_mail_id = models.EmailField(max_length=100)
org_phone_number = models.CharField(max_length=20)
org_address = models.JSONField(max_length=500, null=True)
product = models.ManyToManyField(Product, related_name='products')
org_logo = models.ImageField(upload_to='org_logo/')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "organisation_master"
def __str__(self):
return self.org_name
serializers.py
class Product_Serializers(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id', 'product_name',)
class Organisation_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Organisation
fields = ('org_id', 'org_name', 'org_address', 'org_phone_number', 'org_mail_id','org_logo','org_code','product')
depth = 1
"
While i tried to do POST method for the organisation model I have tried giving the input for product as "product: 5" and "product: {"product_id": 5,"product_name": "time"} in the postman form data but it is showing as
{
"status": "error",
"code": 400,
"data": {
"product": [
"This field is required."
]
},
"message": "success"
}
Views.py
class Organisation_Viewset(DestroyWithPayloadMixin,viewsets.ModelViewSet):
renderer_classes = (CustomRenderer, ) #ModelViewSet Provides the list, create, retrieve, update, destroy actions.
queryset=models.Organisation.objects.all()
parser_classes = [MultiPartParser, FormParser]
serializer_class=serializers.Organisation_Serializers
def create(self, request, *args, **kwargs):
data = request.data
new_organisation= models.Organisation.objects.create(org_name=data["org_name"],org_code = ["org_code"], org_mail_id =data["org_mail_id"],org_phone_number= data["org_phone_number"], org_address=data["org_address"],org_logo = data["org_logo"])
new_organisation.save()
for product in data["product"]:
product_id = models.Product.objects.get(product_id=product["product_id"])
new_organisation.products.add(product_id)
serializer = serializers.Organisation_serializers(new_organisation)
return Response(serializer.data)
I need to post like this product: {"product_id": 5,"product_name": "time"}, what fields are available in the product model it should be posted on this product field.
Can you please suggest me a way as i tried many ways as per my knowledge but it dosen't worked.
you are using a tuple for fields, put a comma behind product in youre fields. If you use a list then dont use an end comma
fields = ('org_id', 'org_name', 'org_address', 'org_phone_number', 'org_mail_id','org_logo','org_code','product',)
depth = 1
Update your serializers to:
class Product_Serializers(serializers.Serializer):
product_id = serializers.IntegerField()
product_name = serializers.CharField(max_length=100)
class Organisation_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Organisation
fields = (
'org_id',
'org_name',
'org_address',
'org_phone_number',
'org_mail_id',
'org_logo',
'org_code',
'product'
)
depth = 1
Update your views as:
class Organisation_Viewset(ModelViewSet):
# ModelViewSet Provides the list, create, retrieve, update, destroy actions.
renderer_classes = (CustomRenderer,)
queryset = Organisation.objects.all()
parser_classes = [MultiPartParser, FormParser, JSONParser]
serializer_class = Organisation_Serializers
def create(self, request, *args, **kwargs):
serializer = Organisation_Serializers(data=request.data)
serializer.is_valid(raise_exception=True)
product_data = serializer.validated_data.pop('product')
does_not_exist = []
product_instances = []
for product in product_data:
try:
product_instance = Product.objects.get(
product_id=product['product_id'],
product_name=product['product_name']
)
product_instances.append(product_instance)
except Product.DoesNotExist:
does_not_exist.append(product)
if len(does_not_exist) > 0:
return Response({
'error': 'Product does not exist',
'does_not_exist': does_not_exist
}, status=400)
organization = Organisation.objects.create(**serializer.validated_data)
for product in product_instances:
organization.product.add(product)
organization.save()
return Response(Organisation_Serializers(organization).data, status=201)
Now we can send the list of product objects for the create API:
curl --location --request POST 'http://localhost:8000/api/organization/' \
--header 'Content-Type: application/json' \
--data-raw '{
"org_id": "12345",
"org_name": "test organization",
"org_address": "test",
"org_phone_number": "12345",
"org_mail_id": "test#te.st",
"org_code": "12345",
"product": [
{
"product_id": 1,
"product_name": "test p1"
},
{
"product_id": 2,
"product_name": "test p2"
}
]
}'
I am new to Django. While I am try to implement serializer for many to many relationship I got problem.I want to serialize a many to many field and need to create a dictionary based on the nested object.
models.py
class ClassRoom(models.Model):
name = models.CharField(max_length=10)
def __str__(self):
return self.name
class Teacher(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField(max_length=50)
class_room = models.ForeignKey(ClassRoom, on_delete=models.CASCADE)
teacher = models.ManyToManyField(Teacher)
def __str__(self):
return self.name
serializers.py
class ClassRoomSerializer(serializers.Serializer):
class Meta:
model = ClassRoom
fields = '__all__'
class TeacherSerializer(serializers.Serializer):
teacher_name = serializers.CharField(source="name", label="Teacher")
class Meta:
model = Teacher
fields = ['name']
class StudentSerializer(serializers.Serializer):
class_room = serializers.CharField(source="class_room.name")
teacher = TeacherSerializer(many=True)
student_name = serializers.CharField(source='name', label="Student")
class Meta:
model = Student
fields = ['student_name', 'teacher', 'class_room']
views.py
class HomeclassView(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
I got the response like this:
{
results: [
{
"class_room": "Class 1",
"teacher": [
{
"teacher_name": "Maria"
},
{
"teacher_name": "sara"
}
],
"student_name": "John"
}
]
}
But I am expecting the result in :
{
results: [
{
"class_room": "Class 1",
"teacher_name": "Maria",
"student_name": "John"
},
{
"class_room": "Class 1",
"teacher_name": "sara",
"student_name": "John"
},
]
}
Please help me to achieve this.
Thanks in advance
you should for on teachers in ModelViewSet:
class HomeclassView(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
flat_response = []
for teacher in response['teacher']:
res = response.copy()
del res['teacher']
res['teacher_name'] = teacher['teacher_name']
flat_response.append(res)
return Response(data=flat_response)
I want to design solution for ordering items. I have endpoint that create orders BUT I need to to have items object in the order. let me show you the code
class ItemModel(models.Model):
name = models.CharField(max_length=50)
price = models.FloatField()
discretion = models.CharField(max_length=500)
available = models.BooleanField(default=True)
class OrderModel(models.Model):
phone = models.CharField(max_length=20)
delevary_time = models.DateTimeField()
class CartModel(models.Model):
order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='order_m')
item = models.ForeignKey(ItemModel, on_delete=models.CASCADE, related_name='item_m')
I need endpoint that create order to me. her what I did
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = CartModel
exclude = ['order',]
depth = 2
class OrderSerializer(serializers.ModelSerializer):
cart = serializers.SerializerMethodField()
class Meta:
model = OrderModel
fields = ['phone', 'state', 'delevary_time', 'cart']
def get_cart(self, obj):
cart = CartModel.objects.filter(order__id=obj.id)
serializer = CartSerializer(cart, many=True)
return serializer.data
this is the endpoint
router.register('order', OrderViewSet, 'api-order')
{
"phone": 124997988698,
"delevary_time": "2020-07-17T19:34:00",
"cart": [
{
"item": 1
},
{
"item": 2
}
]
}
when I post the json it don't save the cart it only save the oder phone and delevary_time. How I can save the cart at the same time
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = CartModel
exclude = ['order',]
depth = 2
class OrderSerializer(serializers.ModelSerializer):
order_m = CartSerializer(many=True) # adding this
class Meta:
model = OrderModel
fields = ['phone', 'state', 'delevary_time', 'order_m']
def create(self, validated_data):
cart_data = validated_data.pop('order_m')
order = OrderModel.objects.create(**validated_data)
for c in cart_data:
CartModel.objects.create(order=order, **c)
return order
Models:
class Person(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveSmallIntegerField()
# More Person fields
class Student(models.Model):
person = models.OneToOneField(
Person, on_delete=models.PROTECT, primary_key=True)
year_of_study = models.PositiveSmallIntegerField()
# More Student fields
Serializers:
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
class StudentSerializer(serializers.ModelSerializer):
person = PersonSerializer()
class Meta:
model = Student
fields = '__all__'
Views:
class StudentView(viewsets.ReadOnlyModelViewSet):
renderer_classes = [JSONRenderer]
parser_classes = [JSONParser]
queryset = Student.objects.all()
serializer_class = StudentSerializer
Requesting single Student:
{
"person": {
"id": 1,
"name": "Example Name",
"age": 20
},
"year_of_study": 3
}
But I need to work with plain structure like:
{
"id": 1,
"name": "Example Name",
"age": 20,
"year_of_study": 3
}
Where (in serializer or in view or somewhere else) and how should I do it?
I only need GET requests (using ReadOnlyModelViewSet because of it). But if would also be nice to know how to create/update/delete such structure as well.
you can create serializer like below
class StudentSerializer(serializers.ModelSerializer):
name = serilizer.CharFiled(source="person.name")
person_id = serilizer.IntegerFiled(source="person.id")
age = serilizer.IntegerFiled(source="person.age")
class Meta:
model = Student
exclude = ('person',)
You have person = PersonSerializer() which outputs the person's data. Remove this line and it'll display just person_id.
Now if you want a combination of GETs returning person data but POSTs using the person_id, you can do the following:
person = PersonSerializer(read_only=True)
person_id = serializers.PrimaryKeyRelatedField(
queryset=Person.objects.all(),
source='person',
write_only=True,
)
I would like to output a field that counts the number of Candidat in Candidat Model. I am currently using the following serializer:
class CountCSerializer(serializers.ModelSerializer):
user_count = serializers.SerializerMethodField()
class Meta:
model = Candidat
fields = ( 'user_count',)
def get_user_count(self, obj):
return Candidat.objects.count()
and the following api:
class CountCViewSet(ModelViewSet):
queryset = Candidat.objects.all()
serializer_class = CountCSerializer
urls.py:
router.register(r'CountC', CountCViewSet, base_name='users-count')
models.py:
class Candidat(models.Model):
name = models.CharField(max_length=50)
lastName = models.CharField(max_length=50)
email = models.CharField(max_length=50)
tel = models.CharField(max_length=50, default=0)
password = models.CharField(max_length=50)
civility = models.CharField(max_length=50)
birthDate = models.DateField(auto_now=False, auto_now_add=False)
gouvernorate = models.CharField(max_length=50)
def __str__(self):
return "Candidat: {}".format(self.name)
But im getting nothing!
Any help in the matter would be much appreciated.
I was looking for the same and noticed that ModelViewSet generates a count by default. You can see it by navigating to the endpoint in a browser or checking the response body in Postman.
Example:
{
"count": 10,
"next": null,
"previous": null,
"results": [
{
"id": 62,
"entity_name": "The company name",
"entity_website_url": "thecompany.com",
"entity_city": "Los Angeles",
"entity_state": "CA"
}
...
]
}