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)
Related
We have the follwing structure (library->books->pages)
the first serializer
class Library(serializers.ModelSerializer):
books = BookSerializer(many=True)
class Meta:
model = Library
fields = '__all__'
#transaction.atomic
def create(self, validated_data):
# create logic here
the second serializer
class BookSerializer(serializers.ModelSerializer):
pages = PageSerializer(many=True, required=False)
class Meta:
model = Book
fields = '__all__'
we have an endpoint library/, where we post the payload of the following format
{
"ref": "43a0c953-1380-43dd-a844-bbb97a325586",
"books": [
{
"name": "The Jungle Book",
"author": "Rudyard Kipling",
"pages": [
{
"content": "...",
"pagenumber": 22
}
]
}
]
}
all the objects are created in the database, but the response does not contain pages key. It looks like this
{
"id": 27,
"ref": "43a0c953-1380-43dd-a844-bbb97a325586",
"books": [
{
"id": 34,
"name": "The Jungle Book",
"author": "Rudyard Kipling"
}
]
}
depth attribute does not seem to work. What do I have to do to make pages appear in the responce?
We can achieve the desired behavior using depth in the class Meta of the BookSerializer.
class BookSerializer(serializers.ModelSerializer):
...
class Meta:
...
depth = 3
Copied from documentation
The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
Another way to get this behavior would be to use serializer.SerializerMethodField for getting the pages of the book serializer.
class BookSerializer(serializers.ModelSerializer):
pages = serializers.SerializerMethodField()
def get_pages(self, obj):
return PageSerializer(obj.page_set.all(), many=True,).data
...
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
Django Rest Framework - Group data by its parent tag
I have 3 serializers, One for tactics, one for techniques, one for sub techniques, and I'll be adding the sub techniques serializer as an explicit field to the techniques serializer.
models
class Tag(models.Model):
_id = models.CharField(max_length=10)
name = models.CharField(max_length=100)
def __str__(self):
return self._id
def __unicode__(self):
return self._id
class ModelA(models.Model):
_id = models.CharField(max_length=10)
title = models.CharField(max_length=100)
tags = models.ManyToManyField(Tag)
queries = ArrayField(models.TextField(),null=True, blank=True, default=list)
def __str__(self):
return self._id
def __unicode__(self):
return self._id
class ModelB(models.Model):
subtitle = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__(self):
return self.subtitle
serializers
# serializers
class TagSerializer(serializers.ModelSerializer):
def to_representation(self, value):
print(value)
return value.name
class Meta:
model = Tag
fields = ('name',)
class QueriesSerializer(serializers.ModelSerializer):
class Meta:
model = ModelB
fields = '__all__'
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 EndUserSerializer(serializers.ModelSerializer):
data = ??
class Meta:
model = ??
fields = '__all__'
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
desired response
{
[
{
"tag": "Biologyl,
"data": [
{
"_id": str
"title": str
"tag": []
"queries": []
}
]
},
{
"tag": "Denoi2,
"data": [
{
"_id": str
"title": str
"tag": []
"queries": []
}
]
},
]
}
actual response
{
"_id": str
"title": str
"tag": []
"queries": []
}
viewset
#viewsets
class BooksViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = ??.objects.all()
serializer_class = EndUserSerializer
You can use django-rest-multiple-models for receiving multiple querysets in the same view.
https://github.com/MattBroach/DjangoRestMultipleModels
It should look like this:
class BooksViewSet(ObjectMultipleModelAPIView):
querylist = [
{
'querylist': ModelA.objects.filter(tags__name="Biologyl"),
'serializer_class': EndUserSerializer,
'label': 'Biologyl',
},
{
'querylist': ModelA.objects.filter(tags__name="Denoi2"),
'serializer_class': EndUserSerializer,
'label': 'Denoi2',
},
....
]
You can also make it dynamic, but it seems a little bit dangerousđź‘€.
class BooksViewSet(ObjectMultipleModelAPIView):
querylist = [
{
'querylist': ModelA.objects.filter(tags=tag),
'serializer_class': EndUserSerializer,
'label': tag.name,
}
for tag in Tag.objects.all()
]
See more https://django-rest-multiple-models.readthedocs.io/en/latest/object-options.html
I have 2 models: Page and Section. A Page object may have 1-to-many Section objects.
In models.py:
class Page(models.Model):
name = models.CharField(max_length=100, help_text='Name of the page.')
class Meta:
ordering = ['id']
class Section(models.Model):
page = models.ForeignKey(Page, on_delete=models.CASCADE)
name = models.CharField(max_length=100, help_text='Name of the section.')
index = models.IntegerField(blank=True, help_text='Index number to sort section.')
class Meta:
ordering = ['id']
index in a Section object indicates its ordering (position) among the rest of Section objects that have same Page object as their foreign key.
In serializers.py:
class SectionSerializer(serializers.ModelSerializer):
page = serializers.PrimaryKeyRelatedField(queryset=Page.objects.all(),
many=False, required=True)
class Meta:
model = Section
fields = ['id', 'page', 'name', 'index']
class PageSerializer(serializers.ModelSerializer):
sections = SectionSerializer(source='section_set', many=True, required=False) # problem is here.
# can I do something like source="section_set.order_by('index')"
class Meta:
model = Page
fields = ['id', 'name', 'sections']
With sections attribute in PageSerializer I can get all Section objects that have the same Page object as foreign key. But not ordered by their index field.
When I query Page.objects.all using PageSerializer I got this result in browsable API:
[
{
"id":1,
"name":"Home",
"sections":[
{
"id":10,
"page":1,
"name":"second_section_for_page_1",
"index":2
},
{
"id":11,
"page":1,
"name":"first_section_for_page_1",
"index":1
},
{
"id":12,
"page":1,
"name":"third_section_for_page_1",
"index":3
}
]
},
{
"id":2,
"name":"Profile",
"sections":[
{
"id":13,
"page":2,
"name":"second_section_for_page_2",
"index":2
},
{
"id":14,
"page":2,
"name":"first_section_for_page_2",
"index":1
}
]
}
]
I would like the result to be ordered by index. Like this:
[
{
"id":1,
"name":"Home",
"sections":[
{
"id":11,
"page":1,
"name":"first_section_for_page_1",
"index":1
},
{
"id":10,
"page":1,
"name":"second_section_for_page_1",
"index":2
},
{
"id":12,
"page":1,
"name":"third_section_for_page_1",
"index":3
}
]
},
{
"id":2,
"name":"Profile",
"sections":[
{
"id":14,
"page":2,
"name":"first_section_for_page_2",
"index":1
},
{
"id":13,
"page":2,
"name":"second_section_for_page_2",
"index":2
}
]
}
]
Can I do something like sections = SectionSerializer(source="section_set.order_by('index')") in PageSerializer class?
I have such serializers
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id',)
class MyModelIdListSerializer(serializers.Serializer):
ids = MyModelSerializer(many=True)
And request body like
{
"ids": [
{
"id": "f07e3673-6631-4139-90da-331773ba868e"
},
{
"id": "f07e3673-6631-4139-90da-331773ba868e"
}
]
}
But need this
{
"ids": [
"f07e3673-6631-4139-90da-331773ba868e",
"f07e3673-6631-4139-90da-331773ba868e"
]
}
I can do ListField with UUID as child but need to write custom validate if such id exists on table
If you want list of ids use PrimaryKeyRelatedField
ids = serializers.PrimaryKeyRelatedField(many=True, read_only=True)