Django Rest Framework -Serializer inside serializer using same instance - django

So, I have a weird legacy issue where I have one model "Data", and for serialization purposes I need to restructure the fields.
class Data(models.Model):
id ...
field_a
field_b
field_c
date
Then I have the serializers:
class DataInfoSerializer(ModelSerializer):
class Meta:
model = Data
fields = ['field_a', 'field_b', 'field_c']
class DataSerializer(ModelSerializer):
data_info = DataInfoSerializer(required=False, read_only=True)
class Meta:
model = Data
fields = ['id', 'date', 'data_info']
Now I somehow have to get DRF to use the same instance of Data that is passed into the "DataSerializer" to render the DataInfoSerializer.
Any ideas how to achieve this? Or a better way.

Use source='*' to pass the entire object to a field, including nested serializers. Docs
class DataSerializer(ModelSerializer):
data_info = DataInfoSerializer(required=False, read_only=True, source='*')
class Meta:
model = Data
fields = ['id', 'date', 'data_info']

Related

Get only one filed from a nested serializer

Her it is my django serializer:
class ShopSerializer(serializers.ModelSerializer):
rest = RestSerializer(many=True)
class Meta:
model = RestaurantLike
fields = ('id', 'created', 'updated', 'rest')
This will wrap whole RestSerializer inside ShopSerializer on response.
How can I get only one or two fields from RestSerializer instead of having all the fields inside ShopSerializer?
Get only two field of RestSerializer instead of whole RestSerializer
If you want to have limited amount of fields from RestSerializer, then you can simply limit it in the fields:
class RestSerializer(serializers.ModelSerializer):
class Meta:
model = Restaurant
fields = ('id', 'name', ...)
If you want just want field, lets say the primary key or Slug, then you can use PrimaryKeyRelatedField or SlugRelatedField for that. For example:
class ShopSerializer(serializers.ModelSerializer):
rest = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='name'
)
class Meta:
model = RestaurantLike
fields = ('id', 'created', 'updated', 'rest')

django-rest ModelSerializer select fields to display in nested relationship

I'm going to reference the django-rest-framework API example on this. Lets say we have two serializers defined as below.
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ['order', 'title', 'duration']
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
Now if i do a GET request and retrieve an Album instance, it will return me a response with a list of Track instances inside it where each instance contains all the fields of Track. Is there a way to return only a selected subset of the fields in the Track model? For example to only return the title and duration field to the client but not the 'order' field.
You can make a specific TrackSerializer for your Album, like:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ['order', 'title', 'duration']
class TrackForAlbumSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ['title', 'duration']
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackForAlbumSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
You do not have to define a single serializer per model, you can define multiple serializers you each use for a dedicated task.

How do you get the PK for a HyperlinkedIdentityField when going through a Many to Many relationship?

I have three models in my Django project:
class Model1(models.Model):
name = models.CharField(max_length=10)
class Model2(models.Model):
name = models.CharField(max_length=10)
class OneToTwo(models.Model):
one = models.ForeignKey(Model1)
two = models.ForeignKey(Model2)
extra_field_necessitating_intermediate_model = models.IntegerField(default=0)
I am using Django REST Framework to build an API for these models, and I have serializers for Model1 and Model2:
class Model1Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Model1
fields = ('url', 'name')
class Model2Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Model2
fields = ('url', 'name')
... so far, so good. The problem arises when I want to nest a Model2 inside the Model1 serializer. I can create
an intermediary serializer for OneToTwo entities containing the Model2 data:
class TwosSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField(source='two.id')
name = serializers.ReadOnlyField(source='two.name')
class Meta:
model = OneToTwo
fields = ('url', 'id', 'name')
class Model2Serializer(serializers.HyperlinkedModelSerializer):
twos = TwosSerializer(source='onetotwo_set', many=True)
class Meta:
model = Model2
fields = ('url', 'name', 'twos')
... but the url field on the TwosSerializer is that of the OneToTwo entity, not of the related Model2 entity.
What I want is the url field of the internal serializer to be the URL to Model2.objects.get(pk=two.id) for
each two object in the relationship.
A naive attempt was to create a url field definition using the HyperlinkedIdentityField class:
# this is within the TwosSerializer class above...
url = serializers.HyperlinkedIdentityField(view_name='model2-detail', lookup_value='two.id')
... but this results in this error:
'OneToTwo' object has no attribute 'two.id'
How do I correctly build the URL for an entity that is passing through an intermediate model that I don't want to
expose in the API?

djangrestframework to display model in foreign key

Please excuse the title. Im not quite sure how ask this question without just showing.
In django I have two models.
class people(models.Model):
name=models.TextField(max_length=100)
nickname=models.TextField(max_length=100)
class visits(models.Model):
person=models.OneToOneField(people)
visitdate=models.DateTimeField(auto_now=True)
and then a serializer for the restapi.
#serializers.py
class VisitsSerializer(serializers.ModelSerializer):
class Meta:
model = visits
fields=("id","person","date")
When the API returns the dictionary, it looks like this.
{id:1,person:1,visitdate:11/23/17}
Is there a way to make the API return the actual values that are associated with the person with id 1? like so.
{id:1,person:{id:1,name:foo,nickname:bar},visitdate:11/23/17}
Try creating a serializer class for People and then add this to your visit serializer:
people = PeopleSerializer(read_only = True)
then add it(people) to fields in the Meta class, and just a suggestion, try making it a foreign key instead of a OnetoOne Relationship
You can do that with nested relationship. Here is an example:
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = people
fields = ('name', 'nickname')
class VisitsSerializer(serializers.ModelSerializer):
person = PersonSerializer(read_only=True)
class Meta:
model = visits
fields = ("id", "person", "date")
Documentation on nested serializers is here.
The Corresponding Serializer would be as follows:
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = people
class VisitsSerializer(serializers.ModelSerializer):
person = serializers.SerializerMethodField()
class Meta:
model = visits
fields = ('id', 'person', 'date')
def get_person(self, obj):
return PersonSerializer(obj.person).data

Add Serializer on Reverse Relationship - Django Rest Framework

I have a Cart model and a CartItem model. The CartItem model has a ForeignKey to the Cart model.
Using Django Rest Framework I have a view where the API user can display the Cart, and obviously then I want to include the CartItem in the respone.
I set up my Serializer like this:
class CartSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
cartitem_set = CartItemSerializer(read_only=True)
class Meta:
model = Cart
depth = 1
fields = (
'id',
'user',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
My problem is the second line, cartitem_set = CartItemSerializer(read_only=True).
I get AttributeErrors saying 'RelatedManager' object has no attribute 'product'. ('product' is a field in the CartItem model. If I exclude product from the CartItemSerializer I just get a new AttributeError with the next field and so on. No matter if I only leave 1 or all fields in the Serializer, I will get a error.
My guess is that for some reason Django REST Framework does not support adding Serializers to reverse relationships like this. Am I wrong? How should I do this?
PS
The reason why I want to use the CartItemSerializer() is because I want to have control of what is displayed in the response.
Ahmed Hosny was correct in his answer. It required the many parameter to be set to True to work.
So final version of the CartSerializer looked like this:
class CartSerializer(serializers.ModelSerializer):
cartitem_set = CartItemSerializer(read_only=True, many=True) # many=True is required
class Meta:
model = Cart
depth = 1
fields = (
'id',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
It's important to define a related name in your models, and to use that related name in the serializer relationship:
class Cart(models.Model):
name = models.CharField(max_length=500)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, related_name='cart_items')
items = models.IntegerField()
Then in your serializer definition you use those exact names:
class CartSerializer(serializers.ModelSerializer):
cart_items = CartItemSerializer(read_only=True)
class Meta:
model = Cart
fields = ('name', 'cart_items',)
It would be wise to share your whole code, that is model and serializers classes. However, perhaps this can help debug your error,
My serializer classes
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = CartItem
fields = ('id')
class CartSerializer(serializers.ModelSerializer):
#take note of the spelling of the defined var
_cartItems = CartItemSerializer()
class Meta:
model = Cart
fields = ('id','_cartItems')
Now for the Models
class CartItem(models.Model):
_cartItems = models.ForeignKey(Subject, on_delete=models.PROTECT)
#Protect Forbids the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
class Meta:
ordering = ('id',)
class Cart(models.Model):
class Meta:
ordering = ('id',)
For a detailed overview of relationships in django-rest-framework, please refer their official documentation