adding one serializer fields to another -django rest framework - python

my serializer.py file is as
...
class RelativeSerializerSLC(serializers.ModelSerializer):
full_name = serializers.CharField(source="user.full_name")
rtl_full_name = serializers.CharField(source="user.rtl_full_name")
gender = serializers.CharField(source="user.gender")
phone = serializers.CharField(source="user.phone")
email = serializers.CharField(source="user.email")
avatar = serializers.CharField(source="user.avatar")
date_of_birth = serializers.CharField(source="user.date_of_birth")
class Meta:
model = Relative
fields = ("full_name", "rtl_full_name", "gender", "phone", "email", "avatar", "date_of_birth", "blood_group", "rel")
read_only_fields = ["patient"]
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "id", "full_name", "rtl_full_name", "gender", "phone", "email", "date_of_birth", "avatar"
there i'm creating other serializer fields(userSerializer) and added to my RelativeSerializer.
that seems uglyyy to me, i have no idea on. is there any better option like using one serializer fields for other.
Thanks, new to DRF :)

Maybe try this:
class RelativeSerializerSLC(serializers.ModelSerializer):
users = UserSerializer(read_only=True)
class Meta:
model = Relative
fields = ("full_name", "rtl_full_name", "gender", "phone", "email", "avatar",
"date_of_birth", "blood_group", "rel")
read_only_fields = ["patient"]
And put UserSerializer class on top of the relativeSerializer. Let me know if it works. I don't see your model fields so it might not be.
Check out for nested serializers here https://www.django-rest-framework.org/api-guide/relations/#nested-relationships

Related

Django Rest Framework - serializers.SlugRelatedField does not return field value

I have the following Car model in my Django Rest Framework project:
from django.db import models
from django.contrib.auth.models import User
class Car(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
driver = models.ForeignKey(User, related_name='cars', on_delete = models.CASCADE)
def __str__(self):
return self.name
class Meta:
ordering = ['created']
As you can see, the Car model has a foreignkey relationship with the built-in User model. The field is called driver.
Now, using the serializer class I also wanted to print out the username of the driver. Therefore, I used serializers.SlugRelatedField like this:
class CarSerializer(serializers.ModelSerializer):
username = serializers.SlugRelatedField(read_only=True, slug_field='username')
class Meta:
model = Car
fields = ['id', 'name', 'username']
But in the JSON output I can not see the username value:
[
{
"id": 1,
"name": "BMW"
},
{
"id": 2,
"name": "Audi"
}
]
What is wrong here?
If you want a custom field SlugField can't be use as from the documents
A RegexField that validates the input against a URL matching pattern.
Expects fully qualified URLs of the form http:///.
Corresponds to django.db.models.fields.URLField. Uses Django's
django.core.validators.URLValidator for validation.
Signature: URLField(max_length=200, min_length=None,
allow_blank=False)
so it doesn't accept slug_field as an argument and this field only use to do regex for urls
This is how you would create a custom field to return from serializer:
class CarSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField() # custom serializer method
def get_username(self, obj):
return obj.driver.username
class Meta:
model = Car
fields = ['id', 'name', 'username']

Add expandable field to Serializer on Self Related Model

I have got a model of User Details which is Self Related to create the relationship between two users.
Model
class AdvDetails(models.Model):
user_id = models.UUIDField(primary_key=True,default=uuid.uuid4,editable=False)
title = models.CharField(max_length=5, choices=[('Mr', 'Mr'), ('Ms', 'Ms'), ('Mrs', 'Mrs'), ('Dr', 'Dr'), ('NA', '')], default='NA')
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email_id = models.EmailField(null=True, blank=True, default=None)
parent_profile = models.ForeignKey("self", null=True, blank=True)
View Set
class AdvDetailsViewSet(viewsets.ModelViewSet):
serializer_class = AdvDetailsSerializer
filter_backends = (filters.SearchFilter,DjangoFilterBackend, filters.OrderingFilter)
filter_fields = ('email_id,'parent_profile')
search_fields = ( '^first_name',)
def get_queryset(self):
return AdvDetails.objects.all()
Serializer
class AdvDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = AdvDetails
fields = '__all__'
This structure facilitates me to help get the data in the below format
{
"user_id": "055cbde6-10ea-4558-86fc-1b42624ce760",
"title": "Mr",
"first_name": "foo",
"last_name": "bar",
"email_id" : "foo#bar.com"
"parent_profile":"6c429d4c-9fb4-42e5-9d7d-9fc782f81fb0"
}
I would like to modify the serializer in such a way that I would get the data of the parent profile's email_id as below
{
"user_id": "055cbde6-10ea-4558-86fc-1b42624ce760",
"title": "Mr",
"first_name": "foo",
"last_name": "bar",
"email_id" : "foo#bar.com",
"parent_profile":"6c429d4c-9fb4-42e5-9d7d-9fc782f81fb0",
"parent_email_id" : "parent#email.com"
}
Any help is highly appreciated. I've tried PrimaryKeyRelatedField but it was not solving my problem.
I made a package that allows expanding fields dynamically, on-demand per request:
https://github.com/rsinger86/drf-flex-fields
For your case:
class AdvDetailsSerializer(FlexFieldsModelSerializer):
class Meta:
model = AdvDetails
fields = ('user_id', 'title', 'first_name', 'last_name', 'email_id', 'parent_profile', 'parent_email_id' )
expandable_fields = {'parent_profile': '<app_name>.AdvDetailsSerializer'}
Replace <app_name> with the name of the Django app that defines the serializer, so it can be loaded lazily.
If instead you would like to statically expand a field, you can define that field as a nested serializer:
class AdvDetailsSerializer(serializers.ModelSerializer):
parent_profile = ProfileProfileSerializer()
class Meta:
model = AdvDetails
fields = (
'user_id',
'title',
'first_name',
'last_name',
'email_id',
'parent_profile',
'parent_email_id'
)

django-rest-framework- accepting id for submitted foreign key field

I have the following models:
class Parent(models.Model):
name = models.CharField()
class Child(models.Model):
name = models.CharField()
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
with serializer:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = Child
fields = ('id', 'name', 'parent',)
read_only_fields = ('id',)
and ModelViewSet:
class ChildViewSet(viewsets.ModelViewSet):
serializer_class = ChildSerializer
permission_classes = (IsAuthenticated,)
queryset = Child.objects.all()
paginator = None
If I query the api, I get a json structure that looks like:
{
"id": 1,
"name": "Child Name",
"parent": 3
}
Which is exactly what I want. However, if I try to PUT the same data back, I get the error:
ValueError: Cannot assign "3": "Child.parent" must be a "Parent" instance.
How can I make a submission like this work? I should be able to submit my data in the same way I receive it from the API.
You can use PrimaryKeyRelatedField:
Example from DRF Docs:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
Your code would most likely be:
class ChildSerializer(serializers.ModelSerializer):
parent = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Child
fields = ('id', 'name', 'parent',)
read_only_fields = ('id',)

Django - InheritanceManager is not working

I have the following simple models.py file:
from django.db import models
from model_utils.managers import InheritanceManager
class Clique(models.Model):
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100, blank=False)
class Post(models.Model):
created = models.DateTimeField(auto_now_add=True)
headline = models.TextField()
clique = models.ForeignKey(Clique,
on_delete=models.CASCADE,
blank=True,
null=True)
objects = InheritanceManager()
def __str__(self):
return self.headline
class VideoPost(Post):
video = models.BooleanField(default=True)
class ImagePost(Post):
image = models.BooleanField(default=True)
So, there is a Clique model which can contain multiple Post instances. The Post instances can be ImagePost or VideoPost. Therefore, ImagePost and VideoPost both inherit Post.
Now, let's say I want to retrieve the ImagePost subclass instances. So, I have the following view in my views.py file:
class PostList(generics.ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
return Post.objects.select_subclasses(ImagePost)
When I pass the endpoint posts/ in the url, then this view will be triggered and it should give me only the ImagePost instances, right ? But I also get the VideoPost instances from the database:
[
{
"clique": "http://127.0.0.1:8000/cliques/1/",
"comment_set": [],
"created": "2019-06-18T09:52:47.929623Z",
"headline": "FirstImagePost",
"id": 1,
"url": "http://127.0.0.1:8000/posts/1/"
},
{
"clique": "http://127.0.0.1:8000/cliques/1/",
"comment_set": [],
"created": "2019-06-18T09:53:20.266653Z",
"headline": "FirstVideoPost",
"id": 2,
"url": "http://127.0.0.1:8000/posts/2/"
}
]
Why is this happening ? I walked through the official doc here . Can somebody help
Just for the sake of completeness, my serializers.py file looks like the following:
from rest_framework import serializers
from posts.models import Post, VideoPost, ImagePost, Clique
class CliqueSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Clique
fields = ('id', 'name', 'post_set')
read_only_fields = ('post_set', )
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
fields = ('url', 'id', 'created', 'headline', 'clique', 'comment_set',)
read_only_fields = ('comment_set',)
class VideoPostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = VideoPost
fields = '__all__'
class ImagePostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ImagePost
fields = '__all__'
From the documentation, it seems like select_subclasses does not filter by subclass type for you, it only converts it to the subclass if it matches what you supplied.
in your case
Post.objects.select_subclasses(ImagePost)
will convert all ImagePost to ImagePost instance, leaving the other ones as Post object, it doesn't filter it out.
from the doc here:
nearby_places = Place.objects.select_subclasses("restaurant")
# restaurants will be Restaurant instances, bars will still be Place instances
In your case you can simply do:
Post.objects.filter(imagepost__image=True).select_subclasses(ImagePost)
Though i don't think you need the select_subclasses(ImagePost) part

How to pull other serialized data from different Serializer classes?

I'm trying to pull the permissions data into my UserSerializer. I have a Many To Many relationship with User to Group with a through table called GroupUser. I'm able to pull the group_id and the group_name, but I can't seem to populate the data for permissions into my User Serializer.
So far I've tried using permission = serializers.ReadOnlyField(source='group.permissions') but that doesn't work.
Any suggestions?
Here's my code:
class GroupSerializer(serializers.ModelSerializer):
users = GroupUserSerializer(source='groupuser_set', many=True)
permissions = PermissionSerializer(source='permission_set', many=True)
class Meta:
model = Group
fields = ('id', 'name', 'users', 'permissions', 'role', )
class GroupUserSerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField(source='group.id')
name = serializers.ReadOnlyField(source='group.name')
class Meta:
model = GroupUser
fields = ( 'id', 'name', )
class UserSerializer(serializers.ModelSerializer):
tests = TestSerializer(source='test_set', many=True)
groups = GroupUserSerializer(source='groupuser_set', many=True)
class Meta:
model = User
fields = ( 'id', 'username', 'groups', 'tests', )
I want my data to look like this:
{
"id": 1,
"username": "user",
"groups": [
{
"id": 2,
"name": "employee"
"permission": "Access-Denied"
}
],
"tests": []
},
but I have it without "permissions"
class UserSerializer(serializers.ModelSerializer):
tests = TestSerializer(source='test_set', many=True)
groups = GroupSerializer(read_only=True, many=True)
class Meta:
model = User
fields = ( 'id', 'username', 'groups', 'tests', )
Edited explanation:
I was able to find the answer with a little tinkering.
I thought, from reading docs and stackoverflow questions, you had to include the 'Through' table when referencing a many to many relationship, but that's not true, the serializer already does it for you.
So I added the groups, as shown, with that information and all the data that is associated in the GroupSerializer with a relationship with data was populated just like the way I want it to be.
I recommend you to write a new Serializer for your UserSerializer, this will not affect other Serializers:
class GroupForUserSerializer(serializers.ModelSerializer):
users = GroupUserSerializer(source='groupuser_set', many=True)
permissions = PermissionSerializer(read_only=True , many=True)
class Meta:
model = Group
fields = ('id', 'name', 'permissions' )
Then you can in your UserSerializer use GroupForUserSerializer now:
class UserSerializer(serializers.ModelSerializer):
tests = TestSerializer(source='test_set', many=True)
groups = GroupForUserSerializer(source='groupuser_set', many=True)
class Meta:
model = User
fields = ( 'id', 'username', 'groups', 'tests' )

Categories