I'm starting with DRF and i would like to serialize both ID and Hyperlinked URL at the same time.
Let me define a simple example model:
class Account(model.Models):
name = models.CharField(max_length=100)
active = models.BooleanField()
I know that there is a ModelSerializer which represents the object as follows:
{
"id": 1,
"name": "library",
"active": true
}
And there is as well an HyperlinkedModelSerializer which represents the object as follows:
{
"url": "http://127.0.0.1:8000/core/accounts/1/",
"name": "library",
"active": true
}
Intrinsically in the HyperlinkedModelSerializer we can retrieve the row's ID, but what I'm looking for is to get something like this:
{
"id": 1,
"url": "http://127.0.0.1:8000/core/accounts/1/",
"name": "library",
"active": true
}
I checked the docs, you can explicitly add the field 'id' by including it in fields.
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['url', 'id', 'name', 'active']
I got the answer from here it works well for me.
Doing this you can avoid to define model's fields and after that define them again in the serializer with the id and url fields like ['url', 'id', 'name', 'active']
With the example it seems dummy but this can save you a lot of time when you deal with models which have much more fields ...
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = [field.name for field in model._meta.fields]
fields.extend(['id', 'url'])
Related
I have a model like below that include JsonField:
class Animal(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_field=15)
data = models.JSONField()
the data field structure is like below:
[
{
"age":"15",
"color":white,
...
},
{
...
...
}
]
And my goal is to show this model in DRF like below:
[
{
"id": 1,
"name": "Bell",
"data": [
{
"age":"15",
"color":"white",
...
},
{
...,
...
}
]
}
]
But when i use JsonFeild in my model my data generated liKe:
[
{
"id": 1,
"name": "Bell",
"data": "[{\"age\":\"15\",\"color\":\"white\",...},{\...,|... }]"
}
]
It's converted to string and have \ in characters.
My serializer:
class AnimalSerializer(serializers.ModelSerializer):
class Meta:
model = Animal
fields = "__all__"
To tell DRF explicitly how to handle the data field you can assign ListField serializer (since as I can see its first level is a list) to it like so:
from rest_framework import serializers
class AnimalSerializer(serializers.ModelSerializer):
data = serializer.ListField()
class Meta:
model = Animal
fields = "__all__"
Optionally, you can pass the child argument into the ListField which is also a serializer, but for the list items so they'll be validated, too.
Or you can use JSONField if it works for you.
Documentation:
https://www.django-rest-framework.org/api-guide/fields/#listfield
https://www.django-rest-framework.org/api-guide/fields/#jsonfield
I found my problem:
I converted my data to json with json.dumps() and assigned the json data to JsonField , the reason of "[{\"..\"}]" was that, i just need to assign my dictionary to JsonField.
I have this serializer that represents content abstract where I would like to add an hyperlink field that is not in the model, but calculated by the framework linked to the ContentsSerializer.
class ContentsAbstractSerializer(serializers.HyperlinkedModelSerializer):
content_url = ???
class Meta:
model = Contents
fields = ('content_url','content_id','content_title', 'content_abstract','start_date','stop_date','last_date','content_status','version')
class ContentsSerializer(serializers.HyperlinkedModelSerializer):
categories = CategoriesContentsSerializer(read_only=True, many=True)
class Meta:
model = Contents
fields = ('content_id','content_title', 'content_abstract', 'content_body','start_date','stop_date','last_date','content_status','version','sections', 'images','attaches','categories')
I would like to have a result like this:
{
"content_url":"http://mysite/Content/125",
"content_id": 125,
"content_title": "this is the title",
"content_abstract": "This is the abstract",
"start_date": "2005-01-12",
"stop_date": "3000-01-12",
"last_date": "2019-02-27T09:40:38Z",
"content_status": "PUBLISHED",
"version": 0
},
I think that instead of define your own custom field you should use the manually specify the view_name for each nested resource as part of extra_kwargs.
I think that you simply could do something like that:
class ContentsAbstractSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contents
fields = ('content_url','content_id','content_title', 'content_abstract','start_date','stop_date','last_date','content_status','version')
extra_kwargs = {
'content_url': {'view_name': 'name_of_your_detail_view'},
}
Output:
{
"content_url":"http://mysite/Content/125",
"content_id": 125,
....
},
The Resource: 'HyperlinkedModelSerializer' with custom nested
'view_name's does not work in combination with
'depth'
Oficial Resource: How hyperlinked views are determined
I have the following models that represent a working group of users. Each working group has a leader and members:
class WorkingGroup(models.Model):
group_name = models.CharField(max_length=255)
leader = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
class WorkingGroupMember(models.Model):
group = models.ForeignKey(WorkingGroup, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
In DRF, I want to efficiently retrieve all groups (there are several hundred) as an array of the following json objects:
{
'id': <the_group_id>
'group_name': <the_group_name>
'leader': <id_of_leader>
'members': [<id_of_member_1>, <id_of_member_2>, ...]
}
To do so, I have set up the following serializer:
class WorkingGroupSerializer(serializers.ModelSerializer):
members = serializers.SerializerMethodField()
class Meta:
model = WorkingGroup
fields = ('id', 'group_name', 'leader', 'members',)
def get_members(self, obj):
return obj.workinggroupmember_set.all().values_list('user_id', flat=True)
So that in my view, I can do something like:
groups = WorkingGroup.objects.all().prefetch_related('workinggroupmember_set')
group_serializer = WorkingGroupSerializer(groups, many=True)
This works, and gives the desired result, however I am finding it does not scale well at all, as the prefetching workinggroupmember_set does not seem to be used inside of the get_members method (Silky is showing a single query to grab all WorkingGroup objects, and then a query for each workinggroupmember_set call in the get_members method). Is there a way to set up the members field in the serializer to grab a flattened/single field version of workinggroupmember_set without using a SerializerMethodField? Or some other way of doing this that lets me properly use prefetch?
Problem here that you are doing values_list on top of all which nullifies your prefetch_related. There is currently no way to do prefetch with values_list see https://code.djangoproject.com/ticket/26565. What you can do is to transition this into python code instead of SQL
class WorkingGroupSerializer(serializers.ModelSerializer):
members = serializers.SerializerMethodField()
class Meta:
model = WorkingGroup
fields = ('id', 'group_name', 'leader', 'members',)
def get_members(self, obj):
return [wgm.user_id for wgm in obj.workinggroupmember_set.all()]
In a recent project with DRF v3.9.1 and django 2.1, I needed to recursively expose all the children of an object, by having only a direct connection to the parent, which could have had multiple children.
Before, if I was to request the "tree" of an object, I was getting:
{
"uuid": "b85385c0e0a84785b6ca87ce50132659",
"name": "a",
"parent": null
}
By applying the serialization shown below I get:
{
"uuid": "b85385c0e0a84785b6ca87ce50132659",
"name": "a",
"parent": null
"children": [
{
"uuid": "efd26a820b4e4f7c8e56c812a7791fcb",
"name": "aa",
"parent": "b85385c0e0a84785b6ca87ce50132659"
"children": [
{
"uuid": "ca2441fc7abf49b6aa1f3ebbc2dae251",
"name": "aaa",
"parent": "efd26a820b4e4f7c8e56c812a7791fcb"
"children": [],
}
],
},
{
"uuid": "40e09c85775d4f1a8578bba9c812df0e",
"name": "ab",
"parent": "b85385c0e0a84785b6ca87ce50132659"
"children": [],
}
],
}
Here is the models.py of the recursive object:
class CategoryDefinition(BaseModelClass):
name = models.CharField(max_length=100)
parent = models.ForeignKey('self', related_name='children',
on_delete=models.CASCADE,
null=True, blank=True)
To get all the reverse objects in the foreign key, apply a field to the serializer class:
class DeepCategorySerializer(serializers.ModelSerializer):
children = serializers.SerializerMethodField()
class Meta:
model = models.CategoryDefinition
fields = '__all__'
def get_children(self, obj):
return [DeepCategorySerializer().to_representation(cat) for cat in obj.children.all()]
Then apply this serializer to a DRF view function or generics class, such as:
re_path(r'categories/(?P<pk>[\w\d]{32})/',
generics.RetrieveUpdateDestroyAPIView.as_view(
queryset=models.CategoryDefinition.objects.all(),
serializer_class=serializers.DeepCategorySerializer),
name='category-update'),
What is the correct way of implementing the following api with django and django-rest-framework. First the model
Model
def ChatModel(models.Model):
users = models.ManyToManyField(User, through=ChatUser)
Response
The desired response when getting this model where the many to many relationship is presented with a nested serializer:
[
{
"id": 1,
"title": "gossip",
"users": [
{
"id": 1,
"name": "George"
},
{
"id": 2,
"name": "Jerry"
}
]
}
]
The request is the critical part: Have the api receive a list of primary keys and create the user -> chat relationship in the backend.
Request
{
"title": "gossip",
"users": [1,2]
}
So how do I allow for such a request to create an object with a many to many relationship.
Edit
I've been trying to implement this using a ChatRequestSerializer class that makes users field something like this
users = serializers.ListField(child=serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all()))
and handle creating the relationships by overriding the create method of the serializer. This doesn't work because it throws the error: 'ManyRelatedManager' object is not iterable
You can use a nested serializer:
class NestedUserSerializer(serializers.ModelSerializer):
class Meta(object):
model = models.User
fields = ('id', 'name')
readonly_fields = ('name',)
class ParentSerializer(serializers.ModelSerializer):
users = NestedUserSerializer(many=True)
class Meta(object):
model = models.Parent
fields = ('id', 'title', 'users')
def create(self, validated_data):
user_ids = {u['id'] for u in validated_data.pop('users', [])}
parent = super(ParentSerializer, self).create(validated_data)
# create users here
return parent
The code is not tested. But I hope it will demonstrate my idea.
Please note that input should look like this:
{
"title": "gossip",
"users": [{"id": 1}, {"id": 2}]
}
Also you can check this answer: django rest framework nested fields with multiple models
I have a model that looks like this:
class Pet(models.Model):
name = models.CharField(max_length=30)
description = models.CharField(max_length=200)
primary_contact = models.ForeignKey(Person)
My Serializer looks like this:
class PetSerializer(serializers.ModelSerializer):
class Meta:
model = Pet
fields = ('id', 'name', 'description', 'primary_contact')
My Viewset:
class PetViewSet(viewsets.ModelViewSet):
queryset = Pet.objects.all()
serializer_class = PetSerializer
My Problem:
If I do a "GET" at my endpoint, my result looks like this:
[
{
"id": 1,
"name": "Ripley",
"description": "Black / Tan Yorkie",
"primary_contact": 1
}
]
The primary_contact only brings back the ID of the Person object. This is exactly how I want the POSTing structure to look like. When I POST, I only want to supply the ID of the Person object. However, when I GET, I want the content to look like this:
[
{
"id": 1,
"name": "Ripley",
"description": "Black / Tan Yorkie",
"primary_contact": {
"id": 1,
"first_name": "MyFistName",
"last_name": "MyLastName",
"phone": "312-xxx-xxxx",
"email": "aarsan#abc123.com"
}
}
]
I can get the above structure by setting depth=2 in my serializer but then if I try to POST, it tries to create the primary_contact, which I not want to do since it already exists.
The workaround I've been using is creating a different endpoint for POST and GET when I have a foreign key, which I hope isn't the only way to do this.
You want a nested serializer. In this case you have to define a serializer for primary contact and link in with primary contact field of PetSerializer.
class ContactSerializer(serializers.ModelSerializer):
class Meta:
model = Contact
fields = ('id', 'field2', 'field3', '...')
class PetSerializer(serializers.ModelSerializer):
primary_contact = ContactSerializer()
class Meta:
model = Pet
fields = ('id', 'name', 'description', 'primary_contact')
You can have several serializers in the same ViewSet, to achieve this you need to overwrite get_serializer_classmethod.
Take a look at this: http://www.django-rest-framework.org/api-guide/generic-views/#get_serializer_classself