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)
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 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)
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?
Context
I have an API endpoint api/v1/checkin/ that returns the current DeviceGroup and the AppVersions for an App that have to be active.
Problem
The endpoint currently returns data along with the correctly filtered AppVersions like this:
{
"customer_device_uuid": "8d252b78-6785-42ea-9aee-b6f9e0f870b5",
"device_group": {
"group_uuid": "869b409d-f281-492e-bb62-d3168aea4394",
"device_group_name": "Default",
"color": "#0a2f45",
"is_default": true,
"app_versions": [
"2.0",
"1.1"
]
}
}
Goal
I want the app_versions in the response to contain more data like this:
{
"customer_device_uuid": "8d252b78-6785-42ea-9aee-b6f9e0f870b5",
"device_group": {
"group_uuid": "869b409d-f281-492e-bb62-d3168aea4394",
"device_group_name": "Default",
"color": "#0a2f45",
"is_default": true,
"app_versions": [
{
"app_version_uuid": "UUID here",
"app_version_name": "1.1",
"package_id": "package name here",
"auto_start": false,
"version_code": 1,
"version_name": "0.1",
"source": "link to file here"
}, ...
]
}
}
Serializers
# serializers.py
class AppVersionSerializer(serializers.ModelSerializer):
auto_start = serializers.BooleanField(source='app_uuid.auto_start')
class Meta:
model = AppVersion
fields = ('app_version_uuid', 'app_version_name', 'package_id', 'auto_start', 'version_code', 'version_name',
'source')
class DeviceGroupSerializer(serializers.ModelSerializer):
app_versions = serializers.SerializerMethodField(read_only=True)
# filters the app versions per app
def get_app_versions(self, model):
qs = model.get_current_app_versions()
return [o.app_version_name for o in qs]
class Meta:
model = DeviceGroup
fields = ('group_uuid', 'device_group_name', 'color', 'is_default', 'app_versions')
class CheckinSerializer(serializers.ModelSerializer):
device_group = DeviceGroupSerializer(many=False, read_only=True, source='group_uuid')
class Meta:
model = CustomerDevice
fields = ('customer_device_uuid', 'customer_uuid', 'device_id_android', 'device_group')
extra_kwargs = {
'customer_uuid': {'write_only': True},
'device_id_android': {'write_only': True}
}
I am guessing that I have to change the get_app_versions() in order to achieve my goal but I have no idea how.
What should I change in order to get the response I want?
EDIT
The get_current_app_versions method that does the filtering
# models.py
def get_current_app_versions(self):
return (
AppVersion.objects
.filter(appversiondevicegrouplink__release_date__lt=timezone.now())
.filter(appversiondevicegrouplink__device_group=self)
.order_by('app_uuid__app_uuid', '-appversiondevicegrouplink__release_date')
.distinct('app_uuid__app_uuid')
)
You are correct in assuming that you will have to change get_app_versions and instead of returning a list in the line return [o.app_version_name for o in qs] you will need to return a list of dictionaries.
You will need to create a full serializer for the AppVersions model. and then in your get_app_versions properly serialize you return values by passing them into your new serializer which contains all the fields you would like to return return AppVersionSerializer2(qs, many=True).data.
You may have to override serialization of certain fields as they may not be handled well by the serializer automatically.
I have 2 model classes:
class CustomUser(AbstractUser):
username = models.CharField(max_length=30, unique=True)
tags = models.ManyToManyField('events.Tag', related_name='user_tag', blank=True)
class Tag(models.Model):
name = models.CharField(unique=True, max_length=50)
And serializers:
class UserSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
class Meta:
...
class TagSerializer(serializers.ModelSerializer):
class Meta:
lookup_field = 'name'
model = Tag
fields = ('id', 'name')
When I do a get query I get something like this:
"data": {
"type": "CustomUser",
"id": "6",
"attributes": {
"username": "mercer",
},
"relationships": {
"tags": {
"data": [
{
"type": "Tag",
"id": "1"
}
]
},
}
}
What I want is to get Tag 'name' field in user representation:
"type": "Tag",
"id": "1",
"name":"name"
And I want to make patch query for adding tag to user.
I can use SerializerMethodField(), but this way I will not able to add tags
The problem was with settings of rest framework. I wrote there custom json render classes and recived this form of output. Now i removed it and everything is fine.