Pass extra arguments to nested Serializer in Django Rest Framework - python

I have such serializer:
class FirstModelSerializer(serializers.ModelSerializer):
secondModel = SecondModelSerializer()
class Meta:
model = FirstModel
fields = '__all__'
Where secondModel is ManyToMany field of FirstModel.
Is there any way to pass FirstModel object id to SecondModelSerializer?

It was easier then I thought. I just had to use context like this
class FirstModelSerializer(serializers.ModelSerializer):
secondModel = SerializerMethodField()
class Meta:
model = FirstModel
fields = '__all__'
def get_secondModel(self, obj):
return SecondModelSerializer(obj.secondModel.all(), many=True, context={'first_model_id': obj.id)).data
And use self.context.get('first_model_id') in SecondModelSerializer to get to this id.

Related

how to customize fields in nested serializer?

class ListSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = JobseekerProfile
fields = ('user',)
*How to modify this fields so that I can add only one field from user like user.username? *
You can add ReadOnlyField Field in the serializer. This field only use when you try to retrieve your data. (GET method)
class ListSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
class Meta:
model = JobseekerProfile
fields = ('user',)
class ListSerializer(serializers.ModelSerializer):
user = serializers.CharField(read_only=True, source='user.username')
class Meta:
model = JobseekerProfile
fields = ('user',)
Try it

DRF Read only field is still validated

I have a field that I want always to be the user. My serializer is like this:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ('user',)
def perform_save(self, serializer):
serializer.save(user=self.request.user)
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
...
But it gives me the error NOT NULL constraint failed: app_my_model.user_id but the field is read_only... I don't get this.
First of all, there is no method named perform_save() for a serializer, it's for the viewset class. This may be the problem
Use the save() method as below
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ('user',)
def save(self, **kwargs):
kwargs['user'] = self.context['request'].user
return super().save(**kwargs)

Django Rest Framework Serializer Doesn't Display ALL Fields

I have a problem where DRF isn't displaying all of my fields correctly for a model class / reference table (specifically the primary key).
My Model Class looks like this (very simple):
class UnitOfIssue(models.Model):
code = models.CharField(max_length=2, primary_key=True)
description = models.CharField(max_length=16)
class Meta:
ordering = ('code',)
def __str__(self):
return "{0} - {1}".format(self.code, self.description)
My Serializer Looks like this:
class UnitOfIssueSerializer(serializers.HyperlinkedModelSerializer):
"""
"""
url = serializers.HyperlinkedIdentityField(
read_only=True,
view_name='unitofissue-detail',
format='html',
lookup_field='code')
class Meta:
model = UnitOfIssue
fields = ('code', 'description', 'url')
# fields = '__all__'
And I'm using a generic view:
class UnitOfIssueDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = UnitOfIssue.objects.all()
serializer_class = UnitOfIssueSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
lookup_field = 'code'
In order for the UnitOfIssue primary key code to be displayed in the auto-generated UI, I have to define fields = ('code', 'description', 'url') in the serializer instead of fields = '__all__'.
I want to just be able to use the '__all__' syntax but I can't figure out what's going wrong.
Also, I'm using Django==1.11.13 and djangorestframework==3.8.2
This issue plagued me for weeks, and yet it was such a simple error. I fixed it by changing the serializer base class from:
class UnitOfIssueSerializer(serializers.HyperlinkedModelSerializer):
to:
class UnitOfIssueSerializer(serializers.ModelSerializer):

Double-nested serializers with Django Rest Framework

Is this possible with DRF using ModelSerializers? I'm mostly interested in seeing an example to reproduce if possible.
Here are my models:
class Grandparent(Model):
g_name = CharField(max_length=10)
parent = ForeignKey('Parent')
class Parent(Model):
p_name = CharField(max_length=10)
child = ForeignKey('Child')
class Child(Model):
c_name = CharField(max_length=10)
Here are my serializers:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = models.Child
fields = ('id', 'c_name')
class ParentSerializer(serializers.ModelSerializer):
child = ChildSerializer()
def create(self, validated_data):
child_data = validated_data.pop('child')
child, _ = models.Child.objects.get_or_create(**child_data)
return models.Parent.objects.create(child=child, **validated_data)
class Meta:
model = models.Parent
fields = ('id', 'p_name', 'child')
class GrandparentSerializer(serializers.ModelSerializer):
parent = ParentSerializer()
class Meta:
model = models.Grandparent
fields = ('id', 'g_name', 'parent')
Here is my grandparent view:
class GrandparentList(generics.ListCreateAPIView):
queryset = models.Grandparent.objects.all()
serializer_class = serializers.GrandparentSerializer
Here is the error I get when I try to post to the Grandparent view:
AttributeError at /grandparents/ 'list' object has no attribute 'get'
Issue we found was that is a bug in the form front-end provided by DRF. Nested serializer behaved correctly when called directly over HTTP.

Django rest framework serializing many to many field

How do I serialize a many-to-many field into list of something, and return them through rest framework? In my example below, I try to return the post together with a list of tags associated with it.
models.py
class post(models.Model):
tag = models.ManyToManyField(Tag)
text = models.CharField(max_length=100)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ("text", "tag"??)
views.py
class PostViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
You will need a TagSerializer, whose class Meta has model = Tag. After TagSerializer is created, modify the PostSerializer with many=True for a ManyToManyField relation:
class PostSerializer(serializers.ModelSerializer):
tag = TagSerializer(read_only=True, many=True)
class Meta:
model = Post
fields = ('tag', 'text',)
Answer is for DRF 3
This is what I did, let´s suppose a Book can have more than one author and an Author can have more than one book:
On Model:
class Author(models.Model):
name = models.CharField(max_length=100, default="")
last_name = models.IntegerField(default=0)
class Book(models.Model):
authors = models.ManyToManyField(Author, related_name="book_list", blank=True)
name = models.CharField(max_length=100, default="")
published = models.BooleanField(default=True)
On Serializers:
class BookSerializer(serializers.ModelSerializer):
authors = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), many=True)
class Meta:
model = Book
fields = ('id', 'name', 'published', 'authors')
class AuthorSerializer(serializers.ModelSerializer):
book_list = BookSerializer(many=True, read_only=True)
class Meta:
model = Author
fields = ('id', 'name', 'last_name', 'book_list')
Adding to #Brian's answer
"tags": [{"name": "tag1"}] can be simplified to "tags": ["tag1", "tag2",...] in this way:
class TagListingField(serializers.RelatedField):
def to_representation(self, value):
return value.name
class PostSerializer(serializers.ModelSerializer):
tag = TagListingField(many=True, read_only=True)
class Meta:
...
More info here: https://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields
The default ModelSerializer uses primary keys for relationships. However, you can easily generate nested representations using the Meta depth attribute:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ("text", "tag")
depth = 1
As mentioned in the 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.
This works for me.
tag = TagSerializer(source="tag", read_only=True, many=True)
Django 2.0
For many to many field, if you want specific one:
class QuestionSerializer(serializers.ModelSerializer):
topics_list = serializers.SerializerMethodField()
def get_topics_list(self, instance):
names = []
a = instance.topics.get_queryset()
for i in a:
names.append(i.desc)
return names
class Meta:
model = Question
fields = ('topics_list',)
In the serializer on init method you can pass the queryset to the field and rest_framework valide the ids on that queryset
1) first extend your serializer from serializers.ModelSerializer
class YourSerializer(serializers.ModelSerializer):
2) include the field on the meta class
class YourSerializer(serializers.ModelSerializer):
class Meta:
fields = (..., 'your_field',)
3) in the init method:
def __init__(self, *args, **kwargs):
super(YourSerializer, self).__init__(*args, **kwargs)
self.fields['your_field].queryset = <the queryset of your field>
You can limit the queryset for that field under any argument using filter or exclude like normally you do. In case that you want include all just use .objects.all()
models.py
class Tag(models.Model):
name = models.CharField(max_length=100)
# ===============
# ... rest of the fields ...
class Post(models.Model):
tag = models.ManyToManyField(Tag)
text = models.CharField(max_length=100)
serialiazers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ("text", "tag")
views.py
## FUNCTION BASED VIEW
def fbvPost_ListView(request):
# list
if request.method == "GET":
posts = Post.objects.all()
serializer = PostSerializer(instance=posts, many=True)
return JsonResponse(serializer.data, safe=False)
return JsonResponse({"success": False})
# ===========================================================
## CLASS BASED VIEW
class cbvPost_ListView(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
NB: Tag, Post are two models & we need to serialize them. Here, Post model have a dependency of Tag models, so here we explicitly mention it, [tags = TagSerializer(many=True, read_only=True)] or its return it's primary field value.
DETAILS HERE
Hi I will be showing many to many for update and create. The context is the event can have many dances and dances can have many event.
The request will be as followed.
{
"competition": 2,
"title": "the title",
"dances":[ {"id":1},{"id":2}],
"description": "the desc"
}
The Create Function will be as followed.
def create(self, validated_data):
try:
dance_ids = []
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_ids.append(dance['id'])
new_event = models.Event.objects.create(**validated_data)
if dance_ids:
for dance_id in dance_ids:
new_event.dances.add(dance_id)
new_event.save()
return new_event
except Exception as e:
raise serializers.ValidationError({'detail': e})
The Update Function will be as followed.
def update(self, instance, validated_data):
# Delete all records of genres.
try:
for current_genre in instance.dances.all():
instance.dances.remove(current_genre)
# Repopulate genres into instance.
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_obj = models.Dance.objects.get(pk=dance['id'])
instance.dances.add(dance_obj)
event_updated = super().update(instance, validated_data)
return event_updated
except Exception as e:
raise serializers.ValidationError({'detail': e})
If you want to just do "dances":[1,2] instead, just make some amendments to the
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_ids.append(dance['id'])
part. I hope this will be able to help yall out! :)
First, Tag needs its own serializer too
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
Then in your PostSerializer, add one line
class PostSerializer(serializers.ModelSerializer):
tag = TagSerializer(read_only=True, many=True).data
class Meta:
model = Post
fields = ("text", "tag")
This will make it so your Tag field in Post is an array of tag ids. if you don't put the ".data" part, it will put all of the attributes of tag, which is probably too much in most cases
You can use serializers.SlugRelatedField() or serializers.StringRelatedField(many=True) Serializer relations
In your case :
class PostSerializer(serializers.ModelSerializer):
tag = serializers.StringRelatedField(many=True) # this will return a list
class Meta:
model = Post
fields = ('tag', 'text',)

Categories