SerializerMethodField and circular import - django

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

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')

How to pass a query parameter to SerializerMethodField in django rest framework?

I want to filter out some instance based on the query parameter I get in the GET call.
class RevisionSerializer(serializers.ModelSerializer):
slot_info = serializers.SerializerMethodField(required=False, read_only=True)
batch_config = serializers.SerializerMethodField(required=False, read_only=True)
class Meta:
model = Revision
fields = ['id', 'status', 'revision_id', 'instructor', 'number_of_classes', 'start_date',
'slot', 'slot_info', 'tinyurl', 'zoom_link', 'batch_config']
read_only_fields = ['revision_id']
def get_batch_config(self, obj):
# filter this on the incoming data from the GET call
related_batches = CompensationClass.objects.select_related('style', 'instructor').filter(
compensation_class_id=obj.revision_id)
batch_config_values = related_batches.values('batch_id', 'age_group', 'level', 'course_version', 'style__name',
'class_number')
return batch_config_values
This is my serializer and I will be passing one date and based on that date I want to filter my serializermethodfield. How can I achieve this?
You can pass parameters to a Serializer using its context:
# views.py
class RevisionView(GenericAPIView):
serializer_class = RevisionSerializer
def get_serializer_context(self):
return {'revision_id': self.request.GET.get('revision_id')}
# serializers.py
class RevisionSerializer(serializers.ModelSerializer):
def get_batch_config(self, obj):
related_batches = CompensationClass.objects.select_related('style', 'instructor').filter(
compensation_class_id=self.context.get('revision_id'))
Thanks to #ThomasGth. I did it like this.
SERIALIZER
class RevisionSerializer(serializers.ModelSerializer):
slot_info = serializers.SerializerMethodField(required=False, read_only=True)
batch_config = serializers.SerializerMethodField(required=False, read_only=True,
)
class Meta:
model = Revision
# fields = '__all__'
fields = ['id', 'status', 'revision_id', 'instructor', 'number_of_classes', 'start_date',
'slot', 'slot_info', 'tinyurl', 'zoom_link', 'batch_config']
read_only_fields = ['revision_id']
def get_batch_config(self, obj):
calendar_date = self.context.get('calendar_date')
related_batches = CompensationClass.objects.select_related('style', 'instructor').filter(
compensation_class_id=obj.revision_id)
if calendar_date:
related_batches = related_batches.filter(calendar_date)
batch_config_values = related_batches.values('batch_id', 'age_group', 'level', 'course_version', 'style__name',
'class_number')
return batch_config_values
VIEWSET
class RevisionViewset(viewsets.ModelViewSet):
queryset = Revision.objects.all().order_by('-modified_at').select_related('instructor', 'slot')
serializer_class = RevisionSerializer
pagination_class = LimitOffsetPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_class = RevisionFilter
def get_serializer_context(self):
context = {'request': self.request}
calendar_date = self.request.GET.get('calendar_date')
if calendar_date:
context['calendar_date'] = calendar_date
return context

Overwrite fields in Django Serializer

I am new in Django and I would like to overwrite the field value in create and update method of serializer. Here is my model=>
class Holiday(models.Model):
HolidayDay = models.DateField()
Created_DT = models.DateTimeField()
Created_Usr = models.CharField(max_length=20)
LastModified_Usr = models.CharField(max_length=20,blank=True)
LastModified_DT = models.DateTimeField(blank=True,null=True)
def __str__(self):
return str(self.HolidayDay)
Here is my serializer=>
class HolidaySerializer(serializers.ModelSerializer):
class Meta:
model=Holiday
fields = [
'id',
'HolidayDay',
'Created_DT',
'Created_Usr',
'LastModified_Usr',
'LastModified_DT'
]
def create(self,validated_data):
validated_data['Created_Usr'] ="Testing"
return Holiday.objects.create(**validated_data)
I would like to update Create_usr field value in create method and LastModified_usr field in update method. But why I can't overwrite the create_usr field as "Testing"?
Here is my views=>
def post(self,request):
holiday = request.data.get('holiday')
serializer = HolidaySerializer(data=holiday)
serializer.is_valid()
print(serializer.errors)
if serializer.is_valid():
holiday_saved=serializer.save()
return Response({"success":"Holiday '{}' created successfully".format(holiday_saved.HolidayDay)})
def put(self,request,pk):
save_holiday = get_object_or_404(Holiday.objects.all(),pk=pk)
data = request.data.get('holiday')
serializer = HolidaySerializer(instance=save_holiday,data=data,partial=True)
if serializer.is_valid(raise_exception = True):
holiday_saved=serializer.save()
return Response({"sucess": "Holiday '{}' updated successfully".format(holiday_saved.HolidayDay)})
Your create method is not defined in your Serializer class, instead it is part of your Meta class. You should be able to solve it by moving your create method to your HolidaySerializer:
class HolidaySerializer(serializers.ModelSerializer):
def create(self,validated_data):
validated_data['Created_Usr'] = "Testing"
return Holiday.objects.create(**validated_data)
class Meta:
model=Holiday
fields = [
'id',
'HolidayDay',
'Created_DT',
'Created_Usr',
'LastModified_Usr',
'LastModified_DT'
]

Django Rest List() Error with multiple serializers

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.

Django Rest Framework serilize relations

How to serialize a fields in related models.
I got a models:
class Order(models.Model):
order_id = models.BigIntegerField(verbose_name='Order ID', unique=True)
order_name = models.CharField(verbose_name='Order name', max_length=255)
order_type = models.IntegerField(verbose_name='Campaign type')
class Types(models.Model):
delimiter = models.CharField(verbose_name='Delimiter', max_length=255)
status = models.BooleanField(verbose_name='Status', default=True)
title = models.CharField(verbose_name='Title', max_length=255)
class User(models.Model):
name = models.CharField(verbose_name='User name', max_length=200, unique=True)
class Report(models.Model):
order = models.ForeignKey(Order, to_field='order_id', verbose_name='Order ID')
user = models.ForeignKey(User, verbose_name='User ID')
ad_type = models.ForeignKey(Types, verbose_name='Type')
imp = models.IntegerField(verbose_name='Total imp')
month = models.DateField(verbose_name='Month', default=datetime.datetime.today)
View:
class ReportLisAPIView(ListAPIView):
serializer_class = ReportSerializer
def get_queryset(self):
month = parse_date(self.kwargs['month']) - relativedelta(day=1)
queryset = (
Report.objects.filter(month=month)
.values_list(
'user', 'user__name', 'order__order_id',
'order__order_name', 'order__order_type'
).all().annotate(Sum('imp'))
)
return queryset
Serializer:
class ReportSerializer(ModelSerializer):
class Meta:
model = Report
depth = 1
I need to get all field like in 'queryset' in get_queryset()
but I got an error:
Got AttributeError when attempting to get a value for field imp on
serializer ReportSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the tuple
instance. Original exception text was: 'tuple' object has no attribute
'imp'.
But if I return in get_queryset() just Report.objects.filter(month=month).all() I'll get all objects and related object with all field, without aggregate of imp and not grouping.
So the question is how to make serializer return structure that set in queryset?
The get_queryset method requires to return a queryset but you are returning a tuple beacause of values_list. Either drop it to return a queryset or go with a more generic view like APIView.
I found a way how to do it.
As I use .values_list() it return list object instead of queryset object. So for serializer do understand what is inside the list I defined all fields in serializer. And in to_representation() I return dictionary like it should be.
Serializer:
class ReportSerializer(serializers.ModelSerializer):
user = serializers.IntegerField()
user_name = serializers.CharField()
order_id = serializers.IntegerField()
order_name = serializers.CharField()
order_type = serializers.IntegerField()
imp = serializers.IntegerField()
class Meta:
model = Report
fields = [
'user', 'user_name', 'order_id', 'order_name',
'order_type', 'imp'
]
depth = 1
def to_representation(self, instance):
Reports = namedtuple('Reports', [
'user',
'user_name',
'order_id',
'order_name',
'order_type',
'imp',
])
return super(ReportSerializer, self).to_representation(
Reports(*instance)._asdict()
)
View:
class ReportLisAPIView(ListAPIView):
serializer_class = ReportSerializer
def get_queryset(self):
month = parse_date(self.kwargs['month']) - relativedelta(day=1)
queryset = (
Report.objects.filter(month=month)
.values_list(
'user', 'user__name', 'order__order_id',
'order__order_name', 'order__order_type'
).all().annotate(Sum('imp'))
)
return queryset
def list(self, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True)
# actualy that's it! part of which is below can be pass and just
# return Response(serializer.data)
result = {
'month': parse_date(self.kwargs['month']).strftime('%Y-%m'),
'reports': []
}
inflcr = {}
for item in serializer.data:
inflcr.setdefault(item['user'], {
'id': item['user'],
'name': item['user_name'],
'campaigns': []
})
orders = {
'id': item['order_id'],
'name': item['order_name'],
'type': item['order_type'],
'impressions': item['imp'],
}
inflcr[item['user']]['campaigns'].append(orders)
result['reports'] = inflcr.values()
return Response(result)