How to create a custom API views in Django Rest Framework - python

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}]}

Related

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')

ManyToManyField value in Django REST Framework

So when I am routing to api/ to view my models I am not seeing what I expected to see.
I wanted to see the names of the strats that I grouped in Strat_Basket model in the variable basket but instead I see their ids which is generated by DJANGO automatically.
I want to see the names rather than numbers in the strat_basket view. It is more informative that's why.
models.py
class Strats(models.Model):
name = models.CharField(max_length=64)
class Strats_Basket(models.Model):
name = models.CharField(max_length=64)
basket = models.ManyToManyField(Strats, blank=True, related_name='list')
serializers.py:
class StratsSerializer(serializers.ModelSerializer):
class Meta:
model = Strats
fields = ('id', 'name')
class Strats_BasketSerializer(serializers.ModelSerializer):
class Meta:
model = Strats_Basket
fields = ('id', 'name', 'basket')
views.py:
class StratsView(viewsets.ModelViewSet):
serializer_class = StratsSerializer
queryset = Strats.objects.all()
class Strats_BasketView(viewsets.ModelViewSet):
serializer_class = Strats_BasketSerializer
queryset = Strats_Basket.objects.all()
urls.py:
router = routers.DefaultRouter()
router.register(r'strats', views.StratsView, 'strats')
router.register(r'strats_basket', views.Strats_BasketView, 'strats_basket')
API OUTPUT:
strats:
[
{
"id": 1,
"name": "strat1"
},
{
"id": 2,
"name": "strat2"
},
{
"id": 3,
"name": "strat3"
},
{
"id": 4,
"name": "strat4"
},
{
"id": 5,
"name": "strat5"
},
{
"id": 6,
"name": "strat6"
}
]
strats_basket:
Instead of 1,2,4 I want to see strat1, strat2, strat4.
[
{
"id": 1,
"name": "Basket 1",
"basket": [
1,
2,
4
]
},
{
"id": 2,
"name": "Basket 2",
"basket": [
3,
5,
6
]
}
]
You can use SerializerMethodField and inside the method return names list using values_list with flat=True
class Strats_BasketSerializer(serializers.ModelSerializer):
basket = serializers.SerializerMethodField()
def get_basket(self, obj):
return obj.basket.all().values_list('name', flat=True)
class Meta:
model = Strats_Basket
fields = ('id', 'name', 'basket')

django rest-framework API returns empty due to end operation how to solve it

I am building an API using django rest-framwork.
this is my data,
[
{
"id": 24,
"name": ""
},
{
"id": 45,
"name": "ADB"
},
{
"id": 2,
"name": "Agriculture"
},
{
"id": 27,
"name": "Category external workforce, professional and business services"
}]
it works fine if I provide a single item for the filter operation. like this.
http://127.0.0.1:8000/op-api/sec/?name=Agriculture
it returns me empty if I provide more then one item for the filtering purpose.
http://127.0.0.1:8000/op-api/sec/?name=Agriculture,ADB
but I want it should return data like this.
[
{
"id": 45,
"name": "ADB"
},
{
"id": 2,
"name": "Agriculture"
}
]
here is my code for the API
class sec_ViewSet(viewsets.ModelViewSet):
http_method_names = ['get']
serializer_class = sector_Serializer
filter_backends = [DjangoFilterBackend , SearchFilter , OrderingFilter]
filterset_fields = ['name',]
search_fields = ['name',]
def get_queryset(self) :
all_sec = Sector.objects.all()
return all_sec
class sector_Serializer(serializers.ModelSerializer) :
class Meta :
model = Sector
fields = ['id' , 'name']
how can I update it so that it works fine.

How to create a child object in Serializer Django Rest?

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
}
},

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

Categories