Django Rest List() Error with multiple serializers - django

Im trying to create a Question Model Serializer. The Question model has several other models that inherit from it like RangeQuestion, YesNoQuestion, etc..
This is my view:
class QuestionViewSet(generics.ListCreateAPIView):
"""
A viewset that provides `create` actions.
"""
queryset = Question.objects.all()
def get_serializer_class(self):
if 'type' in self.request.data:
if self.request.data['type'] == 'range':
return serializers.RangeQuestionSerializer
elif self.request.data['type'] == 'open':
return serializers.QuestionSerializer
elif self.request.data['type'] == 'multiple_choice':
return serializers.MultipleChoiceQuestionSerializer
elif self.request.data['type'] == 'yes_no':
return serializers.YesNoAnswerSerializer
elif self.request.data['type'] == 'object':
return serializers.ObjectQuestionSerializer
else:
return serializers.QuestionSerializer
def create(self, request):
if 'type' in self.request.data:
serializer = self.get_serializer(data=self.request.data)
print serializer
serializer.is_valid(raise_exception=True)
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(
serializer.data,
status=status.HTTP_201_CREATED,
headers=headers)
else:
return Response(status=HTTP_400_BAD_REQUEST, data="Must specify a question type")
And this are my serializers:
class QuestionSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField()
def get_type(self):
return settings.QUESTION_TYPES['open']
class Meta:
model = Question
exclude = []
class YesNoQuestionSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField()
def get_type(self):
return settings.QUESTION_TYPES['yes_no']
class Meta:
model = YesNoQuestion
exclude = []
class RangeQuestionSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField()
def get_type(self):
return settings.QUESTION_TYPES['range']
class Meta:
model = RangeQuestion
exclude = []
class QuestionChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = QuestionChoice
exclude = []
class MultipleChoiceQuestionSerializer(serializers.ModelSerializer):
choices = QuestionChoiceSerializer()
type = serializers.SerializerMethodField()
def get_type(self):
return settings.QUESTION_TYPES['multiple_choice']
class Meta:
model = MultipleChoiceQuestion
exclude = []
When I acces the url of the list action I get the error:
get_type() takes exactly 1 argument (2 given)
Can anyone tell me how to make the list view show all the question serialized according to the subclass?

Your def get_type(self) method should be defined def get_type(self, instance) because rest framework serializers will pass in the instance as the first argument.

Related

DjangoRestFramework, how to add optional field not coming from model to serializer

I have a model like this:
class Camper(models.Model):
location = models.PointField()
name = models.CharField(max_length=255)
and a viewset like this:
class CamperViewSet(viewsets.ModelViewSet):
...
def retrieve(self, request, *args, **kwargs):
"""Retrieve a Camper instance."""
show_weather = request.query_params.get('showWeather', False)
instance = self.get_object()
if show_weather:
lat = instance.location.y
lon = instance.location.x
instance.weather = getWeatherFromLatLon(lat, lon)
serializer = self.get_serializer(instance)
return Response(serializer.data)
So when I request /api/campers/8?showWeather=true I make another request in my view to get the weather from the current position.
How do I add it to my serializer ? It's an optional field so I need to manage this and it's only used in /campers/id so it will not be used in list/create/put/etc
My serializer looks like this:
class CamperSerializer(serializers.ModelSerializer):
camper_id = serializers.IntegerField(source='id')
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location')
you can add custom serializer for retrive only todo it. I called CamperRetriveSerializer.
Inside CamperRetriveSerializer, you can use SerializerMethodField for define field not have in database.
And you want check param show_weather from request, best is pass value of it to context and get it in serializer.
Like this:
class CamperRetriveSerializer(serializers.ModelSerializer):
weather = serializers.SerializerMethodField()
camper_id = serializers.IntegerField(source='id')
def get_weather(self, obj):
show_weather = self.context.get('show_weather')
if show_weather:
lat = obj.location.y
lon = obj.location.x
return getWeatherFromLatLon(lat, lon)
# define default value if not show_weather in this
return ''
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location', 'weather')
class CamperViewSet(viewsets.ModelViewSet):
...
def retrieve(self, request, *args, **kwargs):
"""Retrieve a Camper instance."""
instance = self.get_object()
show_weather = self.request.query_params.get('showWeather', False)
context = {
'show_weather': show_weather
}
serializer = CamperRetriveSerializer(instance, context=context)
return Response(serializer.data)
You can use two different serializers for this.
class CamperViewSet(viewsets.ModelViewSet):
serializer_class = CamperSerializer
def get_serializer_class(self):
serializer_class = self.serialzier_class
if self.request.method == 'GET':
serializer_class = CamperSerializerGet
return serializer_class
#Serializer for GET request
class CamperSerializerGet(serializers.ModelSerializer):
weather = serialziers.SerializerMethodField()
camper_id = serializers.IntegerField(source='id')
def get_weather(self, obj):
return obj.weather
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location', 'weather')
#For other requests call this
class CamperSerializer(serializers.ModelSerializer):
camper_id = serializers.IntegerField(source='id')
class Meta:
model = Camper
fields = ('camper_id', 'name', 'location')

SerializerMethodField and circular import

I need help with REST Framework. I have to do my test for internship position and I have two models with circular import 'Pokemon' model and 'Team' mode. In serializer of 'Team' I have this code
class TeamDetailsSerializer(ModelSerializer):
"""Serializer for details of Team instances"""
pokemon_1 = SerializerMethodField()
pokemon_2 = SerializerMethodField()
pokemon_3 = SerializerMethodField()
pokemon_4 = SerializerMethodField()
pokemon_5 = SerializerMethodField()
trainer = UserSerializer()
class Meta:
model = Team
fields = (
"trainer",
"name",
"pokemon_1",
"pokemon_2",
"pokemon_3",
"pokemon_4",
"pokemon_5",
)
read_only_fields = ("id",)
# Methods to relate each Pokemon object
def get_pokemon_1(self, obj):
pokemon_1 = obj.pokemon_1
if not pokemon_1:
return None
serializer = pokemon.serializers.PokemonDetailsSerializer(pokemon_1)
return serializer.data
def get_pokemon_2(self, obj):
pokemon_2 = obj.pokemon_2
if not pokemon_2:
return None
serializer = pokemon.serializers.PokemonDetailsSerializer(pokemon_2)
return serializer.data
def get_pokemon_3(self, obj):
pokemon_3 = obj.pokemon_3
if not pokemon_3:
return None
serializer = pokemon.serializers.PokemonDetailsSerializer(pokemon_3)
return serializer.data
def get_pokemon_4(self, obj):
pokemon_4 = obj.pokemon_4
if not pokemon_4:
return None
serializer = pokemon.serializers.PokemonDetailsSerializer(pokemon_4)
return serializer.data
def get_pokemon_5(self, obj):
pokemon_5 = obj.pokemon_5
if not pokemon_5:
return None
serializer = pokemon.serializers.PokemonDetailsSerializer(pokemon_5)
return serializer.data
and problem that I get this kind of schema
name* [...]
trainer* User{...}
pokemon_1 integer
nullable: true
pokemon_2 [...]
pokemon_3 [...]
pokemon_4 [...]
pokemon_5 [...]
but I would like to get object type, what kind of solutions I can apply?
Thank a lot

How to pass request as an arguement to serializer in serializer method field

I have a main serializer, and I also have a serializer for my BOOK model so I made a method to return a serialized queryset using my BOOK SERIALIZER
BUT the problem is ** I can't access my main serializer context through BookSerializer**
What can I do?
Views.py:
class BookMainPage(ListModelMixin, RetrieveModelMixin, GenericViewSet):
queryset = Book.objects.select_related('owner').all()
def get_serializer_context(self):
return {'user': self.request.user, 'book_id': self.kwargs.get('pk'), 'request': self.request}
def get_serializer_class(self):
if self.kwargs.get('pk') is None:
return MainPageSerializer
return BookComplexSerializer
MainPageSerializer:
class MainPageSerializer(serializers.Serializer):
#staticmethod
def get_new_books(self):
return BookSimpleSerializer(Book.objects.all().order_by('-created_at')[:10], many=True).data
#staticmethod
def get_popular_books(self):
return BookSimpleSerializer(Book.objects.all().order_by('-bookRate')[:10], many=True).data
#staticmethod
def get_most_borrowed(self):
return BookSimpleSerializer(Book.objects.all().order_by('-wanted_to_read')[:10], many=True).data
new_books = serializers.SerializerMethodField(method_name='get_new_books')
popular_books = serializers.SerializerMethodField(method_name='get_popular_books')
most_borrowed_books = serializers.SerializerMethodField(method_name='get_most_borrowed')
BookSimpleSerializer:
class BookSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'picture']
def get_picture(self, obj: Book):
request = self.context['request']
photo_url = obj.picture.url
return request.build_absolute_uri(photo_url)
picture = serializers.SerializerMethodField(method_name='get_picture')
class CategorySimpleSerializer(serializers.ModelSeria
You can intiialiaze your serializer passing a context with your request in the MethodField method:
class MainPageSerializer(serializers.Serializer):
def get_new_books(self):
return BookSimpleSerializer(
Book.objects.all().order_by('-created_at')[:10],
many=True,
context={"request": self.context["request"]}
).data
def get_popular_books(self):
return BookSimpleSerializer(
Book.objects.all().order_by('-bookRate')[:10],
many=True,
context={"request": self.context["request"]}
).data
def get_most_borrowed(self):
return BookSimpleSerializer(
Book.objects.all().order_by('-wanted_to_read')[:10],
many=True,
context={"request": self.context["request"]}
).data
new_books = serializers.SerializerMethodField(method_name='get_new_books')
popular_books = serializers.SerializerMethodField(method_name='get_popular_books')
most_borrowed_books = serializers.SerializerMethodField(method_name='get_most_borrowed')
And i remove the staticmethod decorator. Your method does not have to be static

sorting search result in django rest framework

I have written a search api for my website in django rest framework.
when you search a name (e.g. "jumanji") there might be more than one result for the search for many reasons. what I want is for the result to be ordered by the "rating" field or "releaseDate" field of the Film model.
here are my codes.
# models.py
class Film(models.Model):
filmID = models.AutoField(primary_key=True)
title = models.CharField(max_length=150)
duration = models.PositiveIntegerField()
typeOf = models.IntegerField(validators=[MaxValueValidator(3), MinValueValidator(1),])
rating = models.FloatField(default=0, validators=[MaxValueValidator(10), MinValueValidator(0),])
releaseDate = models.DateTimeField(null=True)
# serializers.py
class FilmSerializer(serializers.ModelSerializer):
class Meta:
model = Film
fields = [
"filmID", "title", "price", "duration", "typeOf", "numberOfFilminoRatings", "filminoRating", "rating",
"releaseDate", "detailsEn", "salePercentage", "saleExpiration", "posterURL", "posterDirectory",
]
# views.py
'''Override get_search_fields method of SearchFilter'''
class DynamicSearch(filters.SearchFilter,):
def get_search_fields(self,view, request):
return request.GET.getlist('search_fields',[])
'''Override page_size_query_param attribute of PageNumberPagination'''
class CustomizePagination(PageNumberPagination):
page_size_query_param = 'limit'
"""Pagination Handler"""
class PaginationHanlerMixin(object):
#property
def paginator(self):
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator =None
else :
self._paginator = self.pagination_class()
else :
pass
return self._paginator
def paginate_queryset(self,queryset):
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self,data):
if self.paginator is None:
raise "Paginator is None"
return self.paginator.get_paginated_response(data)
class SearchFilm(APIView,PaginationHanlerMixin):
authentication_classes = ()
permission_classes = (AllowAny,)
def __init__(self,):
APIView.__init__(self)
self.search_class=DynamicSearch
self.pagination_class=CustomizePagination
def filter_queryset(self,queryset):
filterd_queryset=self.search_class().filter_queryset(self.request, queryset ,self)
return filterd_queryset
def get(self, request):
films= Film.objects.all()
filtered_queryset=self.filter_queryset(films)
#Get appropriate results for each page
results=self.paginate_queryset(filtered_queryset)
if(results is not None):
serializer=FilmSerializer(results,many=True)
serializer=self.get_paginated_response(serializer.data)
else :
serializer=FilmSerializer(filtered_queryset,many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
It as simple as using function order_by to QuerySet of your films. If you give few options, it will be firstfully ordered by leftmost, then second to the left etc.
films = Film.objects.all().order_by('rating', 'releaseDate')

DRF local variable 'msg_data' referenced before assignment

This is my serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
img_count = serializers.SerializerMethodField('get_img_count')
post_count = serializers.SerializerMethodField('get_post_count')
msg_count = serializers.SerializerMethodField('get_msg_count')
class Meta:
model = User
fields = ('id', 'username', 'img_count', 'post_count', 'msg_count')
def get_img_count(self, obj):
try:
img_data = ImgSerializer(Img.objects.filter(author=obj.id), many=True)
except img_data.DoesNotExist:
return 0
return img_data
def get_post_count(self, obj):
try:
post_data = PostSerializer(Post.objects.filter(author=obj.id), many=True)
except post_data.DoesNotExist:
return 0
return post_data
def get_msg_count(self, obj):
try:
msg_data = Smessage(Msg.objects.filter(author=obj.id), many=True)
except msg_data.DoesNotExist:
return 0
return msg_data
This is my views.py
class UserProfile(APIView):
permission_classes = [AllowAny]
def get(self, request):
query = User.objects.all()
serializer = UserProfileSerializer(query, many=True)
return Response(serializer.data)
This is the Error Snippet
I want to get this
{
"id": 9,
"username": "emil#gmail.com",
"img_count:3,
"post_count":5,
"msg_count":50,
}
also getting error after using img_count.count().
You should change the exceptions to:
img_data.DoesNotExist --> Img.DoesNotExist
post_data.DoesNotExist --> Post.DoesNotExist
msg_data.DoesNotExist --> Msg.DoesNotExist
Because, instances does not have any exception object, rather the model classes has them. More information can be found in documentation.
Update
If you just want the count, then you don't need to use such extensive implementation. You can simply try:
def get_img_count(self, obj):
return Img.objects.filter(author=obj.id).count()
def get_post_count(self, obj):
return Post.objects.filter(author=obj.id).count()
def get_msg_count(self, obj):
return Msg.objects.filter(author=obj.id).count()
You should set default value of each variable because in case of error other than DoesNotExist, you will face this error.
class UserProfileSerializer(serializers.ModelSerializer):
img_count = serializers.SerializerMethodField('get_img_count')
post_count = serializers.SerializerMethodField('get_post_count')
msg_count = serializers.SerializerMethodField('get_msg_count')
class Meta:
model = User
fields = ('id', 'username', 'img_count', 'post_count', 'msg_count')
def get_img_count(self, obj):
img_data = 0
try:
img_data = ImgSerializer(Img.objects.filter(author=obj.id), many=True).data
except Img.DoesNotExist:
pass
return img_data
def get_post_count(self, obj):
post_data = 0
try:
post_data = PostSerializer(Post.objects.filter(author=obj.id), many=True).data
except Post.DoesNotExist:
pass
return post_data
def get_msg_count(self, obj):
msg_data = 0
try:
msg_data = Smessage(Msg.objects.filter(author=obj.id), many=True).data
except Msg.DoesNotExist:
pass
return msg_data