So I have a model called 'Task' and the task has 'UserProfile's working on them. These 'UserProfile' models are just normal Users but inside the App of the 'Task'.
This is the API I have right now:
"tasks": [
{
"id": 1,
"name": "Läs På Internet",
"description": "asdasdasdasa",
"created": "2019-06-08",
"deadline": "2019-06-19",
"state": "new",
"stickers": [
{
"id": 1,
"name": "Detta är en sticker",
"content": "Sillicon Valley",
"created": "2019-06-08",
"creator": {
"id": 1,
"user": 1
}
}
],
"checkmarks": [
{
"id": 1,
"name": "Googla",
"checked": false
}
],
"workers": [
{
"id": 1,
"user": 1
}
]
},
{
"id": 2,
"name": "Läs i böcker",
"description": "aaa",
"created": "2019-06-10",
"deadline": "2019-06-25",
"state": "done",
"stickers": [],
"checkmarks": [],
"workers": [
{
"id": 1,
"user": 1
}
]
}
],
As you can see every user now just has 'id' and 'user', which are both the ID. How do I get the username for every user and display them in the rest api?
# Users
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = 'All Users'
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_data(sender, update_fields, created, instance, **kwargs):
if created:
user = instance
profile = UserProfile.objects.create(user=user)
#Tasks
class Task(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000)
created = models.DateField(default=date.today)
deadline = models.DateField(default=date.today)
state = models.CharField(max_length=20, default='new')
stickers = models.ManyToManyField(Sticker, blank=True)
checkmarks = models.ManyToManyField(Checkmark, blank=True)
workers = models.ManyToManyField(UserProfile, blank=True, related_name='TaskWorkers')
class Meta:
verbose_name_plural = 'Tasks'
def __str__(self):
return "{name}".format(name=self.name)
serializers:
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('id', 'user')
class TaskSerializer(serializers.ModelSerializer):
stickers = StickerSerializer(many=True, read_only=True)
checkmarks = CheckmarkSerializer(many=True, read_only=True)
workers = UserProfileSerializer(many=True, read_only=True)
class Meta:
model = Task
fields = ('id', 'name', 'description', 'created', 'deadline', 'state', 'stickers', 'checkmarks', 'workers')
views:
class TaskView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Task.objects.all()
serializer_class = TaskSerializer
class UserTaskView(TaskView):
def get_queryset(self):
return Task.objects.filter(workers__user=self.request.user)
The User Views are for displaying only for the users that are assigned!
You can either update the user serializer inside your UserProfileSerializer with something like this (creating a serializer with the fields you want for the User):
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = UserProfile
fields = ('id', 'user')
or this (accessing user fields from within the UserProfileSerializer):
class UserProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
class Meta:
model = UserProfile
fields = ('id', 'user', 'username')
As documented below in
https://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations
You can get the user profile details inside the tasks as:
class TaskSerializer(serializers.ModelSerializer):
......
user = UserSerializer()
class Meta:
model = Task
fields = (...., 'user',....., 'workers')
This will return all the fields in the the user model as part of the task nested json
Related
hi,since I am just noobie in DRF I need your help in serializing the Post table.
I have a Post model like this :
class Post(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
title = models.CharField(max_length=255)
descripotion = models.TextField(blank=True)
link = models.CharField(max_length=255, blank=True)
def __str__(self):
return self.title
And then there is a like Model which is related to Post model by FK
class Like(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
post= models.ForeignKey(
'Post',
on_delete=models.CASCADE
)
def __str__(self):
return self.id
and this is my serializers.py for Post table
class PostSerializers(serializers.ModelSerializer):
user = UserSerializer( required = False )
class Meta:
model = Post
fields = [
'id', 'title', 'link','user','descripotion']
read_only_fields = ['id']
this returns the following response
{
"id": 5,
"title": "string",
"link": "string",
"user": {
"email": "admin#mail.com",
"name": "sia"
},
"descripotion": "string"
}
But I want a response to have the Like counts in it
{
"id": 5,
"title": "string",
"link": "string",
"user": {
"email": "admin#mail.com",
"name": "sia"
},
"descripotion": "string",
"like_count": 50
}
How shoud I change the serializer to achieve this goal?
You can define a new callable field with a method as serializer field:
class PostSerializers(serializers.ModelSerializer):
user = UserSerializer(required=False)
like_count = serializers.SerializerMethodField('get_likes_count')
def get_likes_count(self, obj: Post) -> int:
"""
This function is called by the SerializerMethodField.
"""
return obj.like_set.all().count()
class Meta:
model = Post
fields = ['id', 'title', 'link','user','descripotion', 'like_count']
read_only_fields = ['id']
models.py
class Project(models.Model):
project_name = models.CharField(max_length=20)
client= models.ForeignKey(Client,on_delete=CASCADE,related_name="Client1",default=None)
user=models.ManyToManyField(Default_User,related_name='users',default=None)
description=models.TextField()
type=models.TextField()
class Meta:
db_table ='Project'
def __str__(self):
return self.project_name
class Job(models.Model):
job_name=models.CharField(max_length=50)
user= models.ForeignKey(Default_User,on_delete=CASCADE)
project = ChainedForeignKey(Project,chained_field="user", chained_model_field="user",related_name='projects',show_all=False, auto_choose=True, sort=True)
date = models.DateField(max_length=10,default=None)
class Meta:
db_table ='Job'
def __str__(self):
return '{}'.format(self.job_name)
Serializers
class ProjectSerializers(serializers.ModelSerializer):
class Meta:
model= Project
fields= '__all__'
class Job_Serializers(serializers.ModelSerializer):
class Meta:
model= Job
fields = '__all__'
viewsets
class ProjectViewSet(viewsets.ModelViewSet):
authentication_classes =[JWTAuthentication]
permission_classes=(permissions.IsAdminUser,)
queryset=models.Project.objects.all()
serializer_class=serializers.ProjectSerializers
filter_backends = [filters.SearchFilter]
search_fields = ['project_name']
class Job_Viewset(viewsets.ModelViewSet):
renderer_classes = (CustomRenderer, )
authentication_classes =[JWTAuthentication]
permission_classes=(permissions.IsAdminUser,)
queryset=models.Job.objects.all().order_by('-id')
serializer_class=serializers.Job_Serializers
Actually I need to get the project name from the Project model in the Job model. As now as project is a foreign key in Job model it is returning the project_id in the Job model but i need to get the project name along with the id in the job model response. I have tried with the queryset but i couldn't able to get the results which i expected. Need to get ike below results while using get call function.
Result expected:
{
"id": 1,
"job_name": "API for Timesheet",
"date": "2022-03-08",
"user": 2,
"project": 1,
"project_name": timesheet #need to get like this
}
You should change Job_Serializers :
class Job_Serializers(serializers.ModelSerializer):
project = ProjectSerializers()
class Meta:
model= Job
fields = '__all__'
The result will be:
{
"id": 1,
"job_name": "API for Timesheet",
"date": "2022-03-08",
"user": 2,
"project": { "id": 1,
"project_name": timesheet #need to get like this,
...
}
}
Or You can add some fields on the Job_Serializers:
class Job_Serializers(serializers.ModelSerializer):
project_name = serializers.CharField(source='project.project_name')
class Meta:
model= Job
fields = ['job_name', 'id', 'date', 'user', 'project', 'project_name']
The result should be
{
"id": 1,
"job_name": "API for Timesheet",
"date": "2022-03-08",
"user": 2,
"project": 1,
"project_name": timesheet #need to get like this
}
Finally it should better change Job_Serializers to JobSerializers.
class Hotel (models.Model):
name = models.CharField()
country = models.CharField()
city = models.CharField()
street = models.CharField()
class Residence(models.Model):
hotel_id = models.ForeignKey(Hotel, on_delete=models.DO_NOTHING, related_name='hotel')
house_number = models.CharField()
I want to join two models (Hotel and Residence) and then post it. I wrote the folowing code:
serializers.py
class HotelSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Hotel
fields = ['name', 'country', 'city', 'street']
class ResidenceSerializer(serializers.HyperlinkedModelSerializer):
hotel_id = HotelSerializer()
class Meta:
model = Residence
fields = ['house_number', 'hotel_id']
def create(self, validated_data):
return Residence.objects.create(**validated_data)
views.py
class ResidenceViewSet(viewsets.ModelViewSet):
serializer_class = ResidenceSerializer
queryset = Residence.objects.all()
When I try to post data in my api I got the following error: Residence() got an unexpected keyword argument 'hotel_id'. Can someone help me?
Replacing hotel_id with hotel. And still, you can pass hotel from view
class ResidenceSerializer(serializers.HyperlinkedModelSerializer):
hotel = HotelSerializer()
class Meta:
model = Residence
fields = ['house_number', 'hotel']
Also if you are not interested in hyper links(urls) then replace
`serializers.HyperlinkedModelSerializer` with `serializers.ModelSerializer`.
Difference between HyperlinkedModelSerializer and ModelSerializer is serializer is posted here by Serjik
A simple example:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('url', 'name')
bash> http -a admin:yourpassword http://127.0.0.1:8000/users/
"results": [
{
"email": "admin#min.com",
"groups": [
"http://127.0.0.1:8000/groups/1/",
"http://127.0.0.1:8000/groups/2/"
],
"url": "http://127.0.0.1:8000/users/1/",
"username": "admin"
}
]
But if you change
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
The result will be:
"results": [
{
"email": "admin#min.com",
"groups": [
1,
2
],
"url": "http://127.0.0.1:8000/users/1/",
"username": "admin"
}
]
I want to retrieve Student user fields, but I am having a hard time
trying to figure out how to do that I tried reading docs but still
confused
Studentserializer
class StudentSerializer(serializers.Serializer):
class Meta:
model = Student
fields = ['id', 'user', 'name', 'course']
def create(self, validated_data):
Student_det = Student.objects.create(**validated_data)
return Student_det
serializer view
def Student_list(request):
if request.method == 'GET':
stud = Student.objects.all()
serialized = StudentSerializer(stud, many=True)
print(serialized.data)
return JsonResponse(serialized.data, safe=False)
Student Model
class Student (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
course = models.CharField(max_length=255)
You can check here. I think the part about nested serializers is what you need.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id", "first_name", "last_name", ) # and you can add other fields here "email", "date_joined", etc.
class StudentSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Student
fields = ("id", "user", "name", "course", )
This will serialize your Student model like
{
"id": 1,
"user": {
"id": 1,
"first_name": "Example",
"last_name": "User"
}
"name": "student name",
"course": "course name"
}
I am want to serialize the list during update, example making patch call to update below user profile with "cuisine"
Get call
**http://localhost:8000/api/v1/chef/rayees/details**
{
"user": {
"pk": 2,
"username": "rayees",
"email": "rayees.xxxx#yahoo.co.in",
"first_name": "Rayees",
"last_name": "xxxx",
"user_type": "Chef"
},
"first_name": "Rayees",
"last_name": "xxx",
"bio": "TEST BIO",
"chef_cost": "10.00",
"serve_type": "Non-Veg",
"cuisine": [
"South Indian",
"North Indian"
]
}
Here is my serializer class, i think "serializers.SerializerMethodField()" is read only one and "cuisine" not getting updated during patch operation , If want to serialize both get and patch, what should i do here
class ChefProfileDetails(serializers.ModelSerializer):
chef_date = AvailabilityDetails(many=True, read_only=True)
first_name = serializers.CharField(source='user.first_name', read_only=True)
last_name = serializers.CharField(source='user.last_name', read_only=True)
cities = CitySerializer(many=True, read_only=True)
cuisine = serializers.SerializerMethodField()
user = UserDetailsSerializer(read_only=True)
class Meta:
model = Chef
fields = ('user', 'first_name', 'last_name',
'bio', 'chef_cost', 'serve_type', 'cuisine', 'chef_date', 'cities')
def get_cuisine(self, obj):
cuisine_list = []
for i in obj.cuisine.all():
cuisine_list.append(i.cuisine)
return cuisine_list
There is one-to-many connection between Chef and Cuisine models, so try to use RelatedField:
class ChefProfileDetails(serializers.ModelSerializer):
cuisines = serializers.RelatedField(many=True)
class Meta:
model = Chef
fields = ('user', 'first_name', 'last_name',
'bio', 'chef_cost', 'serve_type',
'cuisine', 'chef_date', 'cities')
I think that should work.
You can achieve what you need by defining a Custom Field and then provide your definition of getter and setter in to_representation() and to_internal_value() respectively.
Code:
class CuisineField(serializers.Field):
def to_representation(self, value):
cuisine_list = []
for i in value:
cuisine_list.append(i.cuisine)
return cuisine_list
def to_internal_value(self, data):
""
Do whatever you want to do in patch
""
And then define cuisine as CuisineField in your serializer as
cuisine = CuisineField()
Refer DRF docs for custom fields