serializing image data from diffrent fields - django

From my little knowledge on how serializers work, I know we mostly use modelserializers, and for that, we would have a model for all we want to serialize but how can I join all the images in the different models and then serialize them.
These are my models
class Vendors(models.Model):
title = models.CharField(max_length=50, blank=False)
image = models.ImageField(upload_to='static/vendors', blank=True, null=True)
class Riders(models.Model):
title = models.CharField(max_length=50, blank=False)
image = models.ImageField(upload_to='static/riders', blank=True, null=True)
class VendorsRiders(models.Model):
VendorImg = models.ForeignKey(Vendors, on_delete=models.CASCADE)
RiderImg = models.ForeignKey(Riders, on_delete=models.CASCADE)
This is my serializer
Class VendorsRidersSerializers(models.Model):
Class Meta:
model = VendorsRiders
fields = '__all__'
So, how to get all the images to the endpoint i would specify ? since, I'm a beginner in DRF I also need a suggestion and advice on the best practice to do this. Thank you

With defining fields = '__all__' in a serializer you just have access to properties of your model. One way to serialize your image fields of related models is to define SerializerMethodField Like:
Class VendorsRidersSerializers(models.Model):
rider_image = serializers.SerializerMethodField()
Class Meta:
model = VendorsRiders
fields = '__all__'
def get_rider_image(self, obj):
req = self.context['request']
# build_absolute_uri will convert your related url to absolute url
return req.build_absolute_uri(obj.VendorImg.image.url)
Also note that you should pass the request from your view to context of your serializer to build an absolute url (Example: VendorsRidersSerializers(queryset, many=True, context={'request': self.request}) or override the get_serializer_class method and pass the request to it's context) and then use the data of this serializer.

Related

display only some fields in get api response django serializer

I have an example model which has a fk relation with user model and Blog model. Now I have a get api which only requires certain fields of user to be displayed.
My model:
class Example(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null=True,
related_name="user_examples",
)
blog = models.ForeignKey(
Blog,
on_delete=models.CASCADE,
null=True,
related_name="blog_examples",
)
/................./
Now my view:
class ExampleView(viewsets.ModelViewSet):
queryset = Example.objects.all()
serializer_class = ExampleSerializer
def list(self, request, *args, **kwargs):
id = self.kwargs.get('pk')
queryset = Example.objects.filter(blog=id)
serializer = self.serializer_class(queryset,many=True)
return Response(serializer.data,status=200)
My serializer:
class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = Example
fields = ['user','blog','status']
depth = 1
Now when I call with this get api, I get all example objects that is required but all the unnecessary fields of user like password, group etc . What I want is only user's email and full name. Same goes with blog, I only want certain fields not all of them. Now how to achieve this in a best way??
You will have to specify the required fields in nested serializers. e.g.
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = Blog
fields = ['title', 'author']
class ExampleSerializer(serializers.ModelSerializer):
blog = BlogSerializer()
class Meta:
model = Example
fields = ['user','blog','status']
are you setting depth in serializer's init method or anywhere else? beacause ideally it should only display id's and not anything else. if yes then set depth to zero and use serializer's method field to return data that you need on frontend. I can provide you with example code samples

How do I filter in URL a serializer when it's nested in another serializer In Django?

I'm creating an API that has nested data like in the picture
Now how to search nested data in URL here's my model
class Robot(models.Model):
robot = models.CharField(max_length=100)
short_Description = models.CharField(max_length=200)
status = models.CharField(max_length=20)
parameter = models.CharField(max_length=200)
jenkins_job = models.CharField(max_length=100, default='JenkinsJobName')
jenkins_token = models.CharField(max_length=100, default='JenkinsToken')
def __str__(self):
return self.robot
class assignParameter(models.Model):
parameterName = models.CharField(max_length=100, blank=True)
assignRobot= models.ForeignKey(Robot, on_delete=models.CASCADE, related_name='param', blank=True, null=True)
Here's my serializer.py
from .models import Robot,assignParameter
from rest_framework import serializers
class assignParameterSerializer(serializers.ModelSerializer):
class Meta:
model = assignParameter
fields = ['id', 'parameterName', 'assignRobot']
class RobotSerializer(serializers.ModelSerializer):
param = assignParameterSerializer(many=True, read_only=True)
JenkinJobName = jenkinsHistorySerializer(many=True, read_only=True)
class Meta:
model = Robot
fields = ['id', 'robot', 'short_Description', 'status', 'parameter', 'jenkins_job', 'jenkins_token', 'param']
and here's my view for the api
class RobotViewSet(viewsets.ModelViewSet):
queryset = Robot.objects.all()
serializer_class = RobotSerializer
filter_backends = [filters.DjangoFilterBackend]
filterset_fields = ['robot', 'JenkinJobName__jenkinsBuildNumber']
authentication_classes = [BasicAuthentication]
permission_classes = [IsAuthenticated]
in the API URL if I want to search a particular robot then using this URL URL/?robot=robotname I'm able to search that particular robot. But how can I search particular nested data using URL?
using my view I'm getting search filters like this
But that is not performing any Search. how to achieve that search and what is wrong with my code can someone please help me?
Actually when i search nested serializer only searched value should be present in list other should to be disappear.
Maybe you will need to overwrite your list method in your view, here it is how actually works, you can add whathever you want to filter or maybe you will need to overwrite your queryset depending on the parameters you want.

Django REST framework - reverse ForeignKey relations

I have the following three models structured around the premise of the Survey.
class Survey(models.Model):
...
id = models.UUIDField(_('Id'), primary_key=True, default=uuid.uuid4, editable=False,)
name = models.CharField(_('Name'), max_length=120, blank=True, unique=True)
slug = models.SlugField(_('Slug'), max_length=120, blank=True, unique=True)
description = models.TextField(_('Description'), blank=True)
...
Each Survey can have multiple questions SurveyQuestion:
class SurveyQuestion(models.Model):
...
survey = models.ForeignKey('surveys.Survey', on_delete=models.CASCADE, null=True, blank=True,)
And each SurveyQuestion can have multiple answers SurveyQuestionAnswer:
class SurveyQuestionAnswer(models.Model):
...
survey_question = models.ForeignKey('surveys.SurveyQuestion', on_delete=models.CASCADE, null=True, blank=True,)
For the sake of brevity, imagine my Survey serializers as being as simple as possible:
class SurveySerialializer(serializers.ModelSerializer):
class Meta:
model = Survey
fields = ('__all__')
Effectively, what I have is the following:
class Survey(APIView):
"""
Survey GET request endpoint: fetches Survey
"""
permission_classes = User
def get(self, request, survey_slug):
survey = Survey.objects.get(slug=survey_slug)
serializer = SurveySerializer(survey)
response = get_hug_response(message='Organisation Active Survey Fetched Successfully', data=serializer.data)
return Response(data=response, status=status.HTTP_200_OK)
But, as you could all probably tell, the corresponding surveys.get('slug') fetch only returns the fields in the Survey model. Ideally, I would like to have some sort of fetch for each SurveyQuestion, and within that nested the SurveyQuestionAnswers
Any pro-tips and pointers would be most appreciated.
I have tried a few things, that only throw errors. I'm struggling to know what this type of API relationship is called in DRF so I can't find appropriate example guides to base the same principles from...
Relevant versions:
Django==2.2.1
djangorestframework==3.9.3
Create two serializers, SurveyQuestionAnswerSerializer and SurveyQuestionSerializer
class SurveyQuestionAnswerSerializer(serializers.ModelSerializer):
class Meta:
model = SurveyQuestionAnswer
fields = '__all__'
class SurveyQuestionSerializer(serializers.ModelSerializer):
survey_questionanswers = SurveyQuestionAnswerSerializer(many=True, read_only=True, source="surveyquestionanswer_set")
class Meta:
model = SurveyQuestion
fields = '__all__'
class SurveySerializer(serializers.ModelSerializer):
survey_questions = SurveyQuestionSerializer(many=True, read_only=True, source="surveyquestion_set")
class Meta:
model = Survey
fields = '__all__'
For more info,
1. What is related_name used for in Django?
2. DRF Serializer's source argument

Polymorphic models serializer

I'm using a Polymorphic model for setting up notifications:
My models:
class Notification(PolymorphicModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_by = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="creatednotifications")
created_on = models.DateTimeField(default=timezone.now)
created_for = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="receivednotifications")
read = models.DateTimeField(default=None, null=True, blank=True)
message = models.CharField(default=None, blank=True, null=True, max_length=800)
#property
def total(self):
return self.objects.filter(created_for=self.request.user).count()
#property
def unread(self):
return self.objects.filter(created_for=self.request.user,read=None).count()
#property
def read(self):
return self.objects.filter(created_for=self.request.user).exclude(read=None).count()
class WorkflowNotification(Notification):
# permission_transition = models.ForeignKey(WorkflowStatePermissionTransition, on_delete=models.CASCADE)
action = models.ForeignKey(UserAction, on_delete=models.CASCADE)
Currently i have just one model WorkFlowNotification inheriting from the Polymorphic model,but many would be there in the future.
Im trying to get the count(total) of notifications for the logged in user in the API ..total is given as property field to help in the same
my serializer:
class NotificationSerializer(serializers.ModelSerializer):
total = serializers.ReadOnlyField()
read = serializers.ReadOnlyField()
unread = serializers.ReadOnlyField()
class Meta:
model = Notification
fields = ['id', 'total','read', 'unread']
In the view:
class NotificationsMeta(generics.ListAPIView):
serializer_class = NotificationSerializer
queryset = Notification.objects.all()
When i try to run the server it shows:
Got AttributeError when attempting to get a value for field `total` on serializer `NotificationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `WorkflowNotification` instance.
Original exception text was: Manager isn't accessible via WorkflowNotification instances.
Since you need the 'meta data' only, what is the use of making a model serializer? Or any serializer, for that matter? Serializers will give you serialized instances of the objects of your model. So if you have multiple objects, you will get multiple serialized objects in response.
Just make your view a normal APIView. Since there is no necessity of serializing anything.
class NotificationsMeta(APIView):
def get(self, request, format=None):
qs = Notification.objects.filter(created_for=self.request.user)
response = {
'total': qs.count(),
'read': qs.filter(read=None).count(),
'unread': qs.exclude(read=None).count()
}
return Response(response)
Now remove those property functions from your model.
I didn't test your queries, just copied them from your model. You will need to check if they are working properly. Hope this helps.
I am not sure about how calling a model property who is responsible for querying in model can give appropriate data from serializer. Unfortunately i do have knowledge gap about that. I am thinking about an alternative solution. I hope following should work.
class NotificationSerializer(serializers.ModelSerializer):
total = serializers.serializers.SerializerMethodField()
read = serializers.ReadOnlyField()
unread = serializers.ReadOnlyField()
class Meta:
model = Notification
fields = ['read', 'unread']
def get_total(self, obj):
user = self.context['request'].user
return Notification.objects.filter(created_for=user).count()
If this work then you can able to do similar kind of thing for read and unread too.
In order to get notification for current_user we need to overwrite get_queryset from view.
class NotificationsMeta(generics.ListAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
return Notification.objects.filter(created_for=self.request.user)

How to serialize a API response (in JSON) and modify and add fields to it in Django Rest Framework?

I am getting data from different APIs in DRF. However, to enforce modularity I need to serialize the JSON response and make a 'fake' model for each API endpoint I am calling.
I have already created a model and a serializer for an endpoint, but I need to make another API call while serializing previous response I need to modify some of the fields.
from rest_framework import serializers
from django.db import models
from ..nlp_utils.google_nlp import GoogleNLP
class Search(models.Model):
title = models.CharField(blank=True, default='')
link = models.CharField(blank=True, default='')
snippet = models.CharField(blank=True, default='')
description = models.CharField(blank=True, default='')
sentiment_score = models.FloatField(blank=True, default=0.0)
sentiment_magnitude = models.FloatField(blank=True, default=0.0)
class SearchResultSerializer(serializers.ModelSerializer):
class Meta:
model = Search
fields = ('title', 'link', 'snippet', 'description','sentiment_score', 'sentiment_magnitude')`
here I need to call some more endpoints and populate sentiment_score and sentiment_magnitude
You have two options:
Option 1
You can override to_representation method of serializer. Each serializer has a method called to_representation that will create json response that will be passed to users.
for example:
class SearchResultSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
r = super(TodoSerializer, self).to_representation(instance)
r.update({
'sentiment_score': 'anything you want here'
})
return r
Option 2
Use django rest MethodSerializer fields in your serializer.
class SearchResultSerializer(serializers.ModelSerializer):
sentiment_magnitude = serializers.SerializerMethodField()
class Meta:
model = Search
fields = '__all__'
def get_sentiment_magnitude(self, obj):
sentiment_magnitude = "anything you want here"
return sentiment_magnitude
Rather than defining it in the model, you can directly attach these fields in the serializer like this(using SerializerMethodField):
class SearchResultSerializer(serializers.ModelSerializer):
sentiment_score = serializers.SerializerMethodField()
sentiment_magnitude = serializers.SerializerMethodField()
class Meta:
model = Search
fields = ('title', 'link', 'snippet', 'description','sentiment_score', 'sentiment_magnitude')
def get_sentiment_magnitude(self, obj):
# call external api with search obj which has been stored in your previous call
return data
def get_sentiment_score(self, obj):
# call external api with search obj which has been stored in your previous call
return data
Update
You can use context from any Generic Views or Viewset to pre-populate data. You can try like this:
class YourViewSet(ViewSet):
...
def get_serializer_context(self):
context = super(YourViewSet, self).get_serializer_context()
data = get_it_from_api()
context['sentiment_score'] = data.get('sentiment_score')
context['sentiment_magnitude'] = data.get('sentiment_magnitude')
return context
And use it in serializer like this:
class SearchResultSerializer(serializers.ModelSerializer):
sentiment_score = serializers.SerializerMethodField()
sentiment_magnitude = serializers.SerializerMethodField()
class Meta:
model = Search
fields = ('title', 'link', 'snippet', 'description','sentiment_score', 'sentiment_magnitude')
def get_sentiment_magnitude(self, obj):
return self.context.get('sentiment_magnitude')
def get_sentiment_score(self, obj):
return self.context.get('sentiment_score')
Also, even without using the generic views/viewset, you can still pass extra context like this SearchResultSerializer(instance, context={'sentiment_magnitude': sentiment_magnitude, "sentiment_score": sentiment_score}). Please see the documentation.