How to create a child object in Serializer Django Rest? - python

I have a task, I need to return this json object here:
{
"id": "99999999",
"point": "11111111",
"name": "some name",
"quantity": {
"needed": "10",
"done": "2",
},
}
I must have the quantity field as a child object, but in the Django model quantity_needed and quantity_done must not be child objects. Here is the code:
# model.py
class NeedModel(models.Model):
article = models.ForeignKey(ArticleModel, on_delete=models.CASCADE)
point = models.ForeignKey(PointModel, verbose_name="Hospital", on_delete=models.CASCADE)
quantity_needed = models.PositiveIntegerField(default=0)
quantity_done = models.PositiveIntegerField(default=0)
I tried to change this using the to_representation method, here's what my code looks like:
# serializer.py
class NeedsSerializer(ModelSerializer):
class Meta:
model = NeedModel
fields = ('id', 'point')
def to_representation(self, instance):
data = super(NeedsSerializer, self).to_representation(instance)
data['name'] = instance.article.name
data['quantity'] = {
'needed': instance.quantity_needed,
'done': instance.quantity_done,
},
return data
But as a result, I get a quantity field with a list that contains the object. How to get rid of this list?
{
"id": 6,
"point": 4,
"name": "Бинт гіпсовий 20см х2,7м",
"quantity": [
{
"needed": 12,
"done": 0
}
],
},

In fact, the answer is very simple. You need to create the dictionary as it should and then use the update method
def to_representation(self, instance):
data = super(NeedsSerializer, self).to_representation(instance)
data['name'] = instance.article.name
q = {'quantity': {
'needed': instance.quantity_needed,
'done': instance.quantity_done,
}}
data.update(q)
return data
And here is the result:
{
"id": 2,
"point": 2,
"name": "Захисний костюм",
"quantity": {
"needed": 1000,
"done": 40
}
},

Related

How to create a custom API views in Django Rest Framework

I want to create a custom API view based on existing data.
models.py
class Practice(models.Model):
practice_id = models.BigAutoField(primary_key=True)
score = models.SmallIntegerField(null=True)
correct = models.SmallIntegerField(null=True)
wrong = models.SmallIntegerField(null=True)
not_answered = models.SmallIntegerField(null=True)
class Meta:
managed = True
db_table = 'practice'
def __str__(self):
return str(self.practice_id)
serializers.py
class PracticeSerializer(serializers.ModelSerializer):
class Meta:
model = Practice
fields = ('practice_id',
'score',
'correct',
'wrong',
'not_answered',
)
views.py
#api_view(['GET'])
def practice_detail(request, pk):
try:
practice = Practice.objects.get(pk=pk)
except Practice.DoesNotExist:
return JsonResponse({'message': 'The practice does not exist'}, status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
exercises_serializer = PracticeSerializer(practice)
return JsonResponse(exercises_serializer.data)
With the code above I get the results of the API view as below using practice id 485 :
/api/practice/485
{
practice_id: 485,
score: 10,
correct: 2,
wrong: 3,
not_answered: 0,
}
Now I want to create a custom API view with the result as below :
{
labels: ["Practice"],
datasets: [
{
label: "Correct",
data: [2],
},
{
label: "Wrong",
data: [3],
},
{
label: "Not_answered",
data: [0],
}
]
}
How to do that?
Is possible to achieve that without create new models?
Define the format in Serializer.to_representation() as documented
.to_representation() - Override this to support serialization, for read operations.
class PracticeSerializer(serializers.ModelSerializer):
class Meta:
model = Practice
fields = ('practice_id',
'score',
'correct',
'wrong',
'not_answered',
)
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
return {
"labels": ["Practice"],
"datasets": [
{
"label": "Correct",
"data": instance.correct,
},
{
"label": "Wrong",
"data": instance.wrong
},
{
"label": "Not_answered",
"data": instance.not_answered,
}
]
}
Output:
$ curl http://127.0.0.1:8000/api/practice/485
{"labels": ["Practice"], "datasets": [{"label": "Correct", "data": 2}, {"label": "Wrong", "data": 3}, {"label": "Not_answered", "data": 0}]}

Django: Return all nested Sub Model from a given Model (which isn't directly linked to given model)

I have my models.py as shown below:
from django.db import models
# Create your models here.
class Category(models.Model):
categoryType = models.CharField(max_length=100,blank = False, unique = True, null = False)
def __str__(self):
return self.categoryType
class SubCategory(models.Model):
subcategoryType = models.CharField(max_length=100)
categoryType = models.ForeignKey(Category,on_delete=models.CASCADE, null = True, related_name='category_type')
def __str__(self):
return f'{self.categoryType} :: {self.subcategoryType}'
class Product(models.Model):
productName = models.CharField(max_length=50,blank = False, null = False)
subCategoryType = models.ForeignKey(SubCategory,on_delete=models.SET_NULL, null=True,related_name='product_subcategories')
#categoryType = models.ForeignKey(Category,on_delete=models.SET_NULL, null=True,related_name='product_categories')
def __str__(self):
return f'{self.productName} : {self.subcategoryType}'
I have created a serializer to get all products within a given category as shown below:
class ProductSerializerSpecific(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id','productName')
class SubCategoryProductsSpecific(serializers.ModelSerializer):
products = ProductSerializerSpecific(source='product_subcategories', many=True)
class Meta:
model = SubCategory
fields = ['products']
class CategoryProducts(serializers.ModelSerializer):
products = SubCategoryProductsSpecific(source='category_type', many=True)
class Meta:
model = Category
fields = ('id','products')
My View goes like this:
class ListAllCategoryProducts(viewsets.ReadOnlyModelViewSet):
queryset = Category.objects.all()
serializer_class = CategoryProducts
And finally I registered by route like this:
router.register(r"category_products", views.ListAllCategoryProducts, basename="categories_products")
When I do a GET request to get all products with a given ID as shown below:
GET http://localhost:8000/category_products/1 HTTP/1.1
The output comes as shown below:
{
"id": 1,
"products": [
{
"products": []
},
{
"products": []
},
{
"products": []
},
{
"products": [
{
"id": 1,
"productName": "Dell XPS"
},
{
"id": 2,
"productName": "Macbook Pro"
},
{
"id": 3,
"productName": "Dell Inspiron"
},
{
"id": 4,
"productName": "Lenevo Ideapad"
},
{
"id": 5,
"productName": "Asus"
}
]
}
]
}
Where each dictionary inside list represents the subcategory, but I was interested in getting a result which had just products which would look something like this:
{
"id": 1,
"products": [
{
"id": 1,
"productName": "Dell XPS"
},
{
"id": 2,
"productName": "Macbook Pro"
},
{
"id": 3,
"productName": "Dell Inspiron"
},
{
"id": 4,
"productName": "Lenevo Ideapad"
},
{
"id": 5,
"productName": "Asus"
}
]
}
If you observe carefully the result I am getting basically is category => subcategory => products, whereas I am interested in category => products (where subcategory is fetched from category and products are fetched from subcategory). What should be the way to do the same? Since products aren't directly linked with category rather they need to go via sub category.
In the Category model, we can make a property to return the QuerySet of all related Products with:
class Category(models.Model):
categoryType = models.CharField(max_length=100,blank = False, unique = True, null = False)
#property
def products(self):
return Product.objects.filter(subCategoryType__categoryType=self)
def __str__(self):
return self.categoryType
Then we remove the SubCategoryProductsSpecific serializer in between, this is the item that makes sublists in the response. We can then set the source to:
class ProductSerializerSpecific(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id','productName')
class CategoryProducts(serializers.ModelSerializer):
products = ProductSerializerSpecific(many=True, read_only=True)
class Meta:
model = Category
fields = ('id', 'products')

Django Rest Framework relationship queries in nested serializer

I am having difficulties in implementing nested serializers in Django REST Framework.
I'm building an Online score board, for which I currently have three models and I'm trying to serialize it into a single response.
models.py
class Player(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __str__(self):
return self.first_name
class Event(models.Model):
user = models.ManyToManyField("Player")
name = models.CharField(max_length=50)
desc = models.CharField(max_length=225)
def __str__(self):
return self.name
class LeadBoard(models.Model):
"""model for LeadBoard"""
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='leadboard_player', null=True, blank=True)
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='leadboard_event', null=True, blank=True)
score = models.IntegerField()
def __str__(self):
return self.score
The event model represent some kind of sports events and each Event can have multiple Players (user filed in Event model), the LeadBoard model stores the score of players in each event.
serializer.py
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = ('id','first_name', 'last_name', 'bio')
class EventSerializer(serializers.ModelSerializer):
user = PlayerSerializer(read_only=True, many=True)
class Meta:
model = Event
fields = ('id', 'name', 'user', 'desc')
class LeadBoardSerializer(serializers.ModelSerializer):
event = EventSerializer(read_only=True)
class Meta:
model = LeadBoard
fields = ('id', 'event', 'score')
I have added two Players
[
{
"id": 1,
"first_name": "Jhon",
"last_name": "Doe"
},
{
"id": 3,
"first_name": "Anna",
"last_name": "Doe"
}
]
and their scores in LeadBoard model
[
{
"id": 1,
"player": 1,
"event": 1,
"score": 20
},
{
"id": 2,
"player": 3,
"event": 1,
"score": 90
}
]
This is the response of LeadBoard,
[
{
"id": 1,
"event": {
"id": 1,
"name": "event2020",
"user": [
{
"id": 1,
"first_name": "Jhon",
"last_name": "Doe"
},
{
"id": 3,
"first_name": "Anna",
"last_name": "Doe"
}
],
"desc": "event description"
},
"score": 20
},
{
"id": 2,
"event": {
"id": 1,
"name": "event2020",
"user": [
{
"id": 1,
"first_name": "Jhon",
"last_name": "Doe"
},
{
"id": 3,
"first_name": "Anna",
"last_name": "Doe"
}
],
"desc": "event description"
},
"score": 90
}
]
But what I'm expecting to get is a response like this, which returns the Players(users) of events and their scores correctly.
[
{
"id": 1,
"event": {
"id": 1,
"name": "event2020",
"user": [
{
"id": 1,
"first_name": "Jhon",
"last_name": "Doe",
"score": 20
},
{
"id": 3,
"first_name": "Anna",
"last_name": "Doe",
"score": 90
}
],
"desc": "event description"
}
}
]
What am I missing here?
I'm new to Django and Django Rest Framework.
You need make response by event not by leadboard.
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = ('id','first_name', 'last_name', 'bio')
class LeadBoardSerializer(serializers.ModelSerializer):
player = PlayerSerializer(read_only=True)
class Meta:
model = LeadBoard
fields = ('id', 'player', 'score')
class EventSerializer(serializers.ModelSerializer):
leadboard_event = LeadBoardSerializer(read_only=True, many=True)
class Meta:
model = Event
fields = ('id', 'name', 'desc', 'leadboard_event')
now use view to get event list
Update 1
if you want get score in player.
class PlayerSerializer(serializers.Serializer):
player_id = serializers.IntegerField()
first_name = serializers.CharField(max_length=256)
last_name = serializers.CharField(max_length=256)
score = serializers.IntegerField()
class LeadBoardSerializer(serializers.ModelSerializer):
player = serializers.SerializerMethodField()
class Meta:
model = LeadBoard
fields = ('id', 'player')
def get_player(self,obj):
player_dict = {'player_id': obj.player.id, 'first_name': obj.player.first_name, 'last_name': obj.player.last_name, 'score': obj.score}
result = PlayerSerializer(data=player_dict)
return result
class EventSerializer(serializers.ModelSerializer):
leadboard_event = LeadBoardSerializer(read_only=True, many=True)
class Meta:
model = Event
fields = ('id', 'name', 'desc', 'leadboard_event')
Try it.

Django Rest Framework - Get full related objects in list

I'm rather new to Django Rest Framework and I'm trying to use DRF to to serialize a list of (related) objects.
I have the following models:
class Answer(models.Model):
value = models.CharField(max_length=128)
class User(models.Model):
name = models.CharField(max_length=128)
age = models.PositiveIntegerField()
class UserAnswer(models.Model):
user = models.ForeignKey(User)
answer = models.ForeignKey(Answer)
And the result I'm trying to get is in this form:
[
{
"name": "myName1",
"answers": [
{
"value": "myFirstAnswer"
},
{
"value": "mySecondAnswer"
},
{
"value": "myThirdAnswer"
},
]
},
{
"name": "myName2",
"answers": [
{
"value": "myFirstAnswer"
},
{
"value": "mySecondAnswer"
},
{
"value": "myThirdAnswer"
},
]
}
]
I'm trying to do it this way now:
class UserAnswerSerializer(serializers.ModelSerializer):
answers = AllUserAnswersSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ('name', 'answers')
But then I get the following result:
[
{
"name": "myName1"
},
{
"name": "myName2"
}
]
And when I try to do it this way:
class UserAnswerSerializer(serializers.ModelSerializer):
answers = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = User
fields = ('name', 'answers')
Then i get the following result (an example again):
[
{
"name": "myName1",
"answers": [
1,
2,
3
]
},
{
"name": "myName2",
"answers": [
4,
5,
6
]
}
]
I'm having a hard time making this work, hope someone can show me how to convert the Primary Key's to actual objects!
Thanks!
Remove the explicit definition of the answers field in your serializer and add depth=1. It should look like this:
class UserAnswerSerializer(serializers.ModelSerializer):
class Meta:
depth = 1
model = User
fields = ('name', 'answers')
Info about depth: http://www.django-rest-framework.org/api-guide/serializers/#specifying-nested-serialization

How to get the fields of Foreign key field in a queryset

I have the following models:
class Product(models.Model):
objects = ProductManger()
name = models.CharField(max_length=200)
brand_name = models.CharField(max_length=100)
description = models.TextField()
image_url = models.CharField(max_length=200)
class Cart(models.Model):
user = models.ForeignKey(User)
creation_date = models.DateTimeField(auto_now_add=True)
modification_date = models.DateTimeField(auto_now=True, auto_now_add=True)
is_check_out = models.BooleanField(default=False)
class CartItem(models.Model):
objects = CartItemManager()
cart = models.ForeignKey(Cart)
product = models.ForeignKey(Product)
quantity = models.PositiveSmallIntegerField(default=1)
I want to get a JSON of all the cartItems in a cart (where is_check_out =False), and for that i have the following code:
def get_user_cart(self, request, **kwargs):
self.method_check(request, allowed=['get'])
self.is_authenticated(request)
cart, created = Cart.objects.get_or_create(user__exact=request.user, is_check_out=False)
if not created:
items = CartItem.objects.filter(cart=cart)
d = []
for item in items:
print item.product
d.append(model_to_dict(item))
data = [ { 'cart_id':cart.id, 'items':d} ]
else:
data = [ { 'cart_id':cart.id, 'items':[]} ]
data_string = json.dumps(data, cls=DjangoJSONEncoder)
return HttpResponse(data_string, mimetype='application/json')
This gives me the following JSON:
[
{
"items": [
{
"product": 48,
"price": "0.69",
"tax": "0.09",
"cart": 169,
"note": null,
"id": 467,
"quantity": 1
}
],
"cart_id": 169
}
]
But what i am looking for is something like :
[
{
"items": [
{
"product": {
"id": 1,
"name": "apple",
"image_url": "http://localhost"
},
"price": "0.69",
"tax": "0.09",
"cart": 169,
"note": null,
"id": 467,
"quantity": 1
}
],
"cart_id": 169
}
]
UPDATE:
I changed my get_user_cart to the following to achieve what i was looking for; if there is a better way of doing this please comment or answer.
def get_user_cart(self, request, **kwargs):
self.method_check(request, allowed=['get'])
self.is_authenticated(request)
cart, created = Cart.objects.get_or_create(user__exact=request.user, is_check_out=False)
if not created:
items = CartItem.objects.select_related('product').filter(cart=cart)
d = []
for item in items:
d.append({
"product": {
"id": item.product.id,
"name": item.product.name,
"image_url": item.product.image_url
},
'price': item.price,
'tax': item.tax,
'cart':item.cart.id,
'note': item.note,
'id':item.id,
'quantity':item.quantity
})
data = [ { 'cart_id':cart.id, 'items':d} ]
else:
data = [ { 'cart_id':cart.id, 'items':[]} ]
data_string = json.dumps(data, cls=DjangoJSONEncoder)
return HttpResponse(data_string, mimetype='application/json')
Any help is appreciated
Instead of using filter, it would appear that you need to use select_related so that the query gets that data as well.
Based on your code, you only need to change one line in the loop:
items = CartItem.objects.select_related('product').filter(cart=cart)

Categories