JsonField in DRF - python

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.

Related

Serialize ID and URL at the same time Django Rest Framework

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

How to get attribute of related object set in DRF serializer?

I am trying to nest data from a single attribute of a related set in one of my DRF serializers.
So far, I am only able to do it through an intermediary serializer, which seems messy. I would like to do it directly.
As of now, this is the structure of my result:
[
{
"id": 1,
"name": "Pinot Noir",
"wine_list": [
{
"id": 1,
"wine": {
"id": 1,
"name": "Juan Gil"
}
}
]
}
]
The extra "wine_list" is redundant and surely unnecessary. I have achieved this with the following code.
These are my models. I have simplified them to include the necessary information only.
class Varietal(models.Model):
name = models.CharField(max_length=50)
class Wine(models.Model):
name = models.CharField(max_length=100)
class WineVarietal(models.Model):
wine = models.ForeignKey(Wine, on_delete=models.CASCADE)
varietal = models.ForeignKey(Varietal, on_delete=models.CASCADE)
These are the serializers I am working with now.
class VarietalDetailWineSerializer(ModelSerializer):
class Meta:
model = Wine
fields = ['id', 'name']
class VarietalDetailWineVarietalSerializer(ModelSerializer):
wine = VarietalDetailWineSerializer()
class Meta:
model = WineVarietal
fields = ['id', 'wine']
class VarietalDetailSerializer(ModelSerializer):
wine_list = VarietalDetailWineVarietalSerializer(source='winevarietal_set', many=True)
class Meta:
model = Varietal
fields = ['id', 'name', 'wine_list']
Ideally, I would get something like this:
[
{
"id": 1,
"name": "Pinot Noir",
"wine": [
{
"id": 1,
"name": "Juan Gil"
}
]
}
]
With code somewhat like this:
class VarietalDetailWineSerializer(ModelSerializer):
class Meta:
model = Wine
fields = [
'id',
'name',
]
class VarietalDetailSerializer(ModelSerializer):
wine = VarietalDetailWineSerializer(source='winevarietal_set.wine', many=True)
class Meta:
model = Varietal
fields = [
'id',
'name',
'wine',
]
But that source value is invalid.
Peace.
One possible way to achieve this is to use serializerMethodField.
class VarietalDetailSerializer(ModelSerializer):
wine = SerializerMethodField()
class Meta:
model = Varietal
fields = [
'id',
'name',
'image',
'description',
'wine',
'active'
]
def get_wine(self, varietal):
wine_varietals = varietal.winevarietal_set.all()
wine = [wine_varietals.wine for wine_varietals in wine_varietals]
return VarietalDetailWineSerializer(instance=wine, many=True).data
Our main target is to add many-to-many fields response from a custom serializer method.

Is it possible to add a calculated hyperlink in a django REST serializer?

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

request with list of id's for many to many relationship in django rest framework

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

Can't get Django Rest Framework GET vs POST structure to differ

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

Categories