django REST framework remove nested json - python

I would like to get the data from multiple model, now I work as these:
class IGStatSerializer(serializers.ModelSerializer):
class Meta:
model = IGStat
fields = [
"account",
"rank",
"followers",
"created",
]
class IGSerializer(serializers.ModelSerializer):
stats = serializers.SerializerMethodField()
class Meta:
model = IGAccount
fields = [
"id",
"username",
"avatar",
"brand",
"stats",]
def get_stats(self, instance):
today = datetime.today() - timedelta(days=1)
stat = instance.stats.all().filter(created__year=today.year,
created__month=today.month,
created__day=today.day)
return IGStatSerializer(stat, allow_null=True, many=True).data
And the json result will be like this:
{
"id": 3613,
"username": "beautyfromnaturehk",
"avatar": "https://scontent-iad3-1.cdninstagram.com/v/t51.2885-19/s320x320/42763479_187833352109784_1648992215864705024_n.jpg?tp=1&_nc_ht=scontent-iad3-1.cdninstagram.com&_nc_ohc=Q4hJvaXL-vYAX--Ol1x&oh=e05aef733557c9951642c3c8b518d2f9&oe=607A54CC",
"brand": 4172,
"stats": [
{
"account": 3613,
"rank": 22822,
"followers": 21485,
"created": "2021-03-16T00:00:00Z"
}
]
},
And in actual case, there will combine more than one models together and having many nested json.
So I would like to remove the nested and rename the field name, like the above case should be like this:
{
"id": 3613,
"username": "beautyfromnaturehk",
"avatar": "https://scontent-iad3-1.cdninstagram.com/v/t51.2885-19/s320x320/42763479_187833352109784_1648992215864705024_n.jpg?tp=1&_nc_ht=scontent-iad3-1.cdninstagram.com&_nc_ohc=Q4hJvaXL-vYAX--Ol1x&oh=e05aef733557c9951642c3c8b518d2f9&oe=607A54CC",
"brand": 4172,
"stats_account": 3613,
"stats_rank": 22822,
"stats_followers": 21485,
"stats_created": "2021-03-16T00:00:00Z"
},
The stats nested was remove and rename the content of it.

Write custom to_representation function in your serializer like this
class IGStatSerializer(serializers.ModelSerializer):
class Meta:
model = IGStat
fields = [
"account",
"rank",
"followers",
"created",
]
class IGSerializer(serializers.ModelSerializer):
stats = serializers.SerializerMethodField()
class Meta:
model = IGAccount
fields = [
"id",
"username",
"avatar",
"brand",
"stats",]
def get_stats(self, instance):
today = datetime.today() - timedelta(days=1)
stat = instance.stats.all().filter(created__year=today.year,
created__month=today.month,
created__day=today.day)
return IGStatSerializer(stat, allow_null=True, many=True).data
def to_representation(self, instance):
data = super().to_representation(instance)
stats = data.pop('stats', None)
# If you are sure that there will be only one stats object
for key, value in stats[0].items():
data['stats_{key}'.format(key=key)] = value
return data

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

how do I group results based on categories?

I want to create a data visualization API where I need to use the results of ASerializer by their given tag in order to categorize each object into categories based on their associated tag if the object contains two tags, then it should be displayed in both . e.g
current output
{
"count":117,
"next":"http://localhost:8002/api/v1/demo/list/?page=2",
"previous":null,
"results":[
{
"_id": "T1189",
"title": "promise",
"tag": [
"Demo",
"Demo2"
],
"queries": [],
}
]
}
desired format
[
{
"Demo": [
{
"_id": "T1189",
"title": "promise",
"tag": [
"Demo",
"Demo2"
],
"queries": [],
}
],
"Demo2": [
{
"_id": "T1189",
"title": "promise",
"tag": [
"Demo",
"Demo2"
],
"queries": [],
}
],
}
]
code
# serializers
class TagSerializer(serializers.ModelSerializer):
def to_representation(self, value):
print(value)
return value.name
class Meta:
model = Tag
fields = ('name',)
class ASerializer(serializers.ModelSerializer):
queries = QueriesSerializer(source='modelb_set', many=True)
tag = TagSerializer(many=True)
class Meta:
model = ModelA
fields = ('_id','title', 'tag','queries',)
class QueriesSerializer(serializers.ModelSerializer):
class Meta:
model = ModelB
fields = '__all__'
#viewsets
class BooksViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = ModelA.objects.all()
serializer_class = ASerializer
If there is an reverse relationship in your database between Tags and ModelA you might be able to do some SubQuery for it. But it looks simple enough for me to do this srot of post processing in the view layer:
class BooksViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
def get(self, *args, **kwargs):
models = ASerializer(ModelA.objects.all(), many=True).data
return_obj = {"Demo": [], "Demo2": []}
for model in models:
for key in return_obj:
if key in model["tag"]:
return_obj[key].append(model)
return JsonResponse(return_obj)

Django : This field is required on POST request

I'm trying to create a serializer which outputs the Report and also the User information.
My task is accomplished by this serializer:
class ReportSerializer(serializers.ModelSerializer):
latitude = serializers.CharField()
longitude = serializers.CharField()
city = serializers.IntegerField()
type = serializers.IntegerField()
# We have created a field which returns a value from get_marker_icon_url
marker_icon = serializers.SerializerMethodField('get_marker_icon_url')
status_str = serializers.SerializerMethodField('convert_status_toStr')
type_str = serializers.SerializerMethodField('convert_type_toStr')
status_color = serializers.SerializerMethodField('get_status_color')
likes = serializers.SerializerMethodField('random_likes')
user = ReportUserSerializer()
class Meta:
model = Reports
fields = [
'user',
'id',
'type',
'city',
'latitude',
'longitude',
'likes',
'type_str',
'status_str',
'status_color',
'attached_message',
'marker_icon',
'attached_photo',
'date_created'
]
...
With this code my serializer returns a response like this:
[
{
"user": {
"id": 1,
"username": "3nematix",
"profile_pic": "http://192.168.0.29:8000/frontend/static/frontend/images/reports/user_profile_pic.jpg",
"verified": false
},
"id": 1,
"type": 9,
"city": 0,
"latitude": "6.5123333",
"longitude": "51.512586",
"likes": 27,
"type_str": "OTHER",
"status_str": "PENDING",
"status_color": "orange",
"attached_message": "test",
"marker_icon": "OTHER",
"attached_photo": "http://192.168.0.29:8000/frontend/static/frontend/images/reports/user_profile_pic_VRjIYTs.jpg",
"date_created": "2020-10-21T23:19:06.899302Z"
},
......
]
And this is exactly what I need, but the problem is that when I'm trying to create a new object by a POST request, I get this response:
{
"user": [
"This field is required."
]
}
If I would remove 'user' from Meta and user = ReportUserSerializer() from the ReportSerializer class, then I can create a new object, but when I wish to get the Reports I with the Users information I need to add these two again, how can I fix it?
You need to fill the user field yourself before calling save.
Here's the easy way:
class YourView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Or slightly differently:
class YourView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super().perform_create(serializer)

Source field in DRF serializer with multiple foreign keys to the same model

I have a Django model as follows:
class Team(models.Model):
name = models.CharField(max_length=100, unique=True)
managers = models.ManyToManyField(User, through=MgrToTeam, related_name='mng')
users = models.ManyToManyField(User, through=UsrToTeam,related_name='usr')
I now have a serializer where i need to display all the users and managers associated with a team:
class TeamDetailSerializer(serializers.ModelSerializer):
managers = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='name')
users = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='name')
class Meta:
model = Team
fields = ['id', 'name', 'managers', 'users']
However, this gives me the same output for both the users and managers.How do i fix this ?
I'm curious as to why you want to use a SlugRelatedField. Assuming you do have a UserSerializer of some sort, would it work for you to do it like this?
class TeamDetailSerializer(serializers.ModelSerializer):
managers = UserSerializer(many=True)
users = UserSerializer(many=True)
class Meta:
model = Team
fields = ['id', 'name', 'managers', 'users']
if you just want Just primary key as below
[
{
"id": 1,
"name": "helo",
"users": [
1,
2
],
"managers":[
1,
2,
]
}
]
class TeamDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Team
fields = ['id', 'name', 'users','managers']
if you want return object properties as below
[
{
"id": 1,
"name": "helo",
"users": [
{
"email": "admin#gmail.com",
"first_name": ""
},
{
"email": "admin1#gmail.com",
"first_name": ""
}
],
"managers": [
{
"email": "admin#gmail.com",
"first_name": ""
},
{
"email": "admin1#gmail.com",
"first_name": ""
}
]
}
]
class TeamDetailSerializer(serializers.ModelSerializer):
users = UserDetailSerializer(many=True)
managers = UserDetailSerializer(many=True)
class Meta:
model = Team
fields = ['id', 'name', 'users', 'managers']

Django Rest Framework: Get related specific model field in serializer from another model

I'm trying to return a Response including data from 2 linked models, where I can get a specific field value from another model. The models are:
class Author(models.Model):
author = models.CharField(max_length=200)
author_created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('author_created',)
class Book(models.Model):
book_name = models.CharField(max_length=200)
author_name = models.ForeignKey(Author, on_delete=models.CASCADE)
book_created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('book_created',)
Serializers are:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('id', 'author')
class BookSerializer(serializers.ModelSerializer):
name = AuthorSerializer(source='author_name')
class Meta:
model = Book
fields = ('id', 'book_name','name')
My response shows like this:
[
{
"id": 1,
"book_name": "Himu",
"name": {
"id": 1,
"author": "Humayun Ahmed"
}
},
{
"id": 2,
"book_name": "Shanta's family",
"name": {
"id": 2,
"author": "Jafar Iqbal"
}
}
]
But i want to show it like this:
[
{
"id": 1,
"book_name": "Himu",
"name": {
"author": "Humayun Ahmed"
}
},
{
"id": 2,
"book_name": "Shanta's family",
"name": {
"author": "Jafar Iqbal"
}
}
]
How can i get this output?
Try something like this name = AuthorSerializer(source='author_name.author')
Reference : http://www.django-rest-framework.org/api-guide/fields/#source

Categories