data in serializer is always empty - python

I have following serializer:
class AdminSerializer(serializers.Serializer):
def validate(self, data):
user = data.get("user_pk")
total_licenses = data.get("total_licenses")
#here i do some validation with the vars
But my data is always empty. This is part of my view
serializer_class = self.get_serializer_class()
serializer = serializer_class(
data=self.request.data,
)
serializer.is_valid(raise_exception=True)
This is my unit test request:
response = self.client.patch(
url,
data={"user_pk": self.user.pk, "total_licenses": 3},
)
Why is my 'data' always empty?

You have to explicitly specify fields in your serializer:
class AdminSerializer(serializers.Serializer):
user_pk = serializers.IntegerField()
total_licenses = serializers.IntegerField()
def validate(self, data):
...

You need to call serializer.save() before you can access .data attribute on a serializer. Otherwise, you can access .validated_data attribute on your serializer

Related

Original exception text was: 'QuerySet' object has no attribute 'client'

I got AttributeError when attempting to get a value for field client on serializer ClientSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
models.py
class Box(models.Model):
box = models.IntegerField()
controller = models.ForeignKey(Controller, related_name='boxes', on_delete=models.CASCADE)
def __str__(self):
return str(self.box)
class Client(models.Model):
client = models.CharField(max_length=30)
cpf = models.IntegerField()
box = models.OneToOneField(
Box,
on_delete=models.CASCADE,
primary_key=True
)
def __str__(self):
return self.client
serializers.py
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = [
"id",
"client",
"cpf",
"box",
]
class BoxSerializer(serializers.ModelSerializer):
class Meta:
model = Box
fields = [
"id",
"box",
"controller"
]
views.py
class ClientViewSet(viewsets.ModelViewSet):
serializer_class = ClientSerializer
queryset = Client.objects.all()
def list(self, request, store_pk=None, locker_pk=None, controller_pk=None, box_pk=None):
queryset = Client.objects.filter(box=box_pk, box__controller=controller_pk, box__controller__locker=locker_pk, box__controller__locker__store=store_pk)
serializer = ClientSerializer(queryset, context={'request': request})
return Response(serializer.data)
def retrieve(self, request, store_pk=None, locker_pk=None, controller_pk=None, box_pk=None):
queryset = Client.objects.filter(box=box_pk, box__controller=controller_pk, box__controller__locker=locker_pk, box__controller__locker__store=store_pk)
client = get_object_or_404(queryset)
serializer = ClientSerializer(client, context={'request': request})
return Response(serializer.data)
I'm trying to get the object client on lockers/1/controllers/1/boxes/1/client/
which is OneToOneField relations with boxes and It's in a nested router
I already tried use decorator #action but yet didn't work.
Anyone know why it's not finding the correct object attribute ?
For a list method you should use many=True parameter when you're creating a new serializer instance:
serializer = ClientSerializer(queryset, context={'request': request}, many=True)
In case of retrieve only one object should be received. Instead of
client = get_object_or_404(queryset)
you should call first(), last() (or most basically and clearly - .get(pk=pk)) on queryset to retrieve only one item from QuerySet. Then you should just execute:
# client is one of elements of your queryset
serializer = ClientSerializer(client, context={'request': request})

Django DRF change serializer data with CreateListModelMixin

I have this class view, but I am unable to modify the serializer data to insert more data (which is needed and needs to be populated automatically).
Because I am creating many instances at once, the serializer is based on kwargs['many'] = True.
Any idea on how I can add another field to each serializer data?
Thanks,
:
class ReservedServiceView(CreateListModelMixin, ModelViewSet):
queryset = ReservedService.objects.all()
serializer_class = ReservedServiceSerializer
authentication_classes = (authentication.TokenAuthentication,)
def perform_create(self, serializer):
# Create an event that is a Reflection of the Reserved Service
serializer_data = self.request.data
for reserved_service in serializer_data:
print("--------",reserved_service, flush=True)
service_id = reserved_service['original_service']
original_service = Service.objects.get(pk=service_id)
calendar_event = CalendarEvent()
calendar_event.name = original_service.name
calendar_event.description = original_service.description
calendar_event.date = reserved_service['date']
calendar_event.type_id = 1
calendar_event.start_time = reserved_service['start_time']
calendar_event.end_time = reserved_service['end_time']
calendar_event.location_id = original_service.location.id
calendar_event.save()
reserved_service['associated_event'] = calendar_event.id
print("**********1", serializer_data)
print("**********2", self.request.data)
serializer.save()
Based in:
class CreateListModelMixin(object):
def get_serializer(self, *args, **kwargs):
""" if an array is passed, set serializer to many """
if isinstance(kwargs.get('data', {}), list):
kwargs['many'] = True
return super(CreateListModelMixin, self).get_serializer(*args, **kwargs)
I am not able to properly get your question, but if your question is that you are not getting the extra fields which you added to the serializer in the response of the view, then here is the answer for it.
The response of this view is returned by create method of CreateModelMixin which passes serializer.data to the data param of Response. You cannot update serializer.data because it is an immutable object. So, to solve this you will have to override the create method as follows:
class ReservedServiceView(CreateListModelMixin, ModelViewSet):
queryset = ReservedService.objects.all()
serializer_class = ReservedServiceSerializer
authentication_classes = (authentication.TokenAuthentication,)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
my_data = {}.update(serializer.validated_data)
# Now you can work over the my_data and add extra fields to it and save it
# and instead of passing serializer.data we pass my_data to Response class
headers = self.get_success_headers(serializer.data)
return Response(my_data, status=status.HTTP_201_CREATED, headers=headers)

Convert URL of an object to its database instance

I created an extra function in my View that receives a list with hyperlinked references to some ResourceGroup objects, but I don't know how to convert them to database instances
class ResourceViewSet(viewsets.ModelViewSet):
queryset = Resource.objects.all()
serializer_class = ResourceSerializer
#action(methods=['put'], detail=True)
def groups_append(self, request, pk=None):
instance = self.get_object()
groups = request.data.get("groups")
for resource_group in groups:
instance.groups.add(WHAT_HERE(resource_group))
instance.save()
return Response(self.get_serializer(instance, many=False).data)
This is the request:
PUT http://.../api/resources/1/groups_append/
with body:
{"groups": ["http://.../api/resource_groups/1/", ...]}
ResourceSerializer:
class ResourceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Resource
fields = ('resource_id', 'object_id', 'type', 'system', 'path', 'groups', 'job_set')
def update(self, instance, validated_data):
instance.object_id = validated_data.get('object_id', instance.object_id)
instance.type = validated_data.get('type', instance.type)
instance.system = validated_data.get('system', instance.system)
instance.path = validated_data.get('path', instance.path)
instance.save()
return instance
ResourceGroupSerializer:
class ResourceGroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ResourceGroup
fields = ('resource_group_id', 'label', 'resource_set')
def update(self, instance, validated_data):
instance.label = validated_data.get('label', instance.label)
instance.save()
return instance
use HyperlinkedRelatedField for groups in ResourceSerializer or just create a new serializer for this action(the main idea is to get the data using a serializers not just directly from the request body) like this:
class ResourceSerializer(serializers.HyperlinkedModelSerializer):
groups = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='groups-detail' ## name of the groups detail url
)
class Meta:
model = Resource
....
then edit your action as below:
#action(methods=['put'], detail=True)
def groups_append(self, request, pk=None):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
### then get the groups from the validated data
groups = serializer.validated_data.get('groups', [])
....
....
References:
1- hyperlinkedrelatedfield

How to set user_id field using JWT in DRF

I want to set user_id field using JWT token and store in database table when new reservation is created. there can be single or multiple reservation request.
whenever user create reservation i want to store there user_id in our table. currently there is no foreign key associated with it. it is simply an integer field.
I am able to fetch user_id from JWT.but its not updating in database
I know this question had been asked previously i tried all the answer of previous post but its not working for me. i dont know why
model.py
class reservations(models.Model):
pet_id=models.IntegerField()
user_id=models.IntegerField(default=0)
location=models.PositiveSmallIntegerField()
arrival=models.DateTimeField()
depature=models.DateTimeField()
comments=models.TextField(max_length=200)
view.py
class requestReservation(CreateAPIView):
serializer_class = requestReservationSerailizer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
serializer = requestReservationSerailizer(data=request.data,context={'user_id': request.user.id}, many=True)
if not serializer.is_valid(raise_exception=False):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({"message":"Success","status_message":"Reservation Created Successfully"},status=status.HTTP_200_OK)
serializer.py
class requestReservationSerailizer(ModelSerializer):
user_id = SerializerMethodField('set_user_id')
class Meta:
model=reservations
fields = [
'pet_id',
'user_id',
'location',
'arrival',
'depature',
'comments',
]
def set_user_id(self, obj):
obj.user_id = self.context.get("user_id")
return obj.user_id
currently it is simply storing user_id as 0 which is default set in model.
SerializerMethodField is read-only by default, here's a quick look at the source code:
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
kwargs['source'] = '*'
kwargs['read_only'] = True
super(SerializerMethodField, self).__init__(**kwargs)
Assuming you want to read and write into this field; remove the SerializerMethodField overriding from the serializer declaration; and set the user_id in your view
class requestReservationSerailizer(ModelSerializer):
class Meta:
model=reservations
fields = [
'pet_id',
'user_id',
'location',
'arrival',
'depature',
'comments',
]
def create(self, request, *args, **kwargs):
data = request.data.copy()
for datum in data:
datum['user_id'] = request.user.id
serializer = requestReservationSerailizer(data=data, many=True)
if not serializer.is_valid(raise_exception=False):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({"message":"Success","status_message":"Reservation Created Successfully"},status=status.HTTP_200_OK)
Ofcourse; if you don't want your view to be doing this (and I'd agree with you here), then pass it in context (explicit) or use self.request.user.id and override validate_user_id
class requestReservationSerailizer(ModelSerializer):
class Meta:
model=reservations
fields = [
'pet_id',
'user_id',
'location',
'arrival',
'depature',
'comments',
]
def validate_user_id(self, value):
user_id = self.context.get('user_id', None) # Assuming you continue to pass it in context
if user_id is None:
# Handle error
return user_id
# You can also do this; might raise an AttributeError if the user is not authenticated:
# return self.request.user.id

Django RestFramework Serializer request.data with array object

i have this situation, i received by POST this data:
{'fields':[{'key':'comment', 'value':'something', 'data_type':'string'},
{'key':'days', 'value':'2', 'data_type':'int'}]}
My serializers
class FieldSerializer(serializers.Serializer):
value = serializers.CharField(max_length=200)
data_type = serializers.CharField(max_length=200)
key = serializers.CharField(max_length=200)
class FieldsSerializer(serializers.Serializer):
fields = FieldSerializer(many=True)
In my view pass request.data to FieldsSerializer()
serializer = FieldsSerializer(data=request.data)
serializer.is_valid()
raise Exception(serializer.data, serializer.errors)
output:
Exception: (ReturnDict([('fields', [])]), ReturnDict())
i use versions Django==1.8.15 and djangorestframework==3.0
you has single data for your FieldsSerializer, so you just need to remove many=True while initial the serializer.
serializer = FieldsSerializer(data=request.data)
# ^^^^^^
or as universal solution
many = isinstance(request.data, list)
serializer = FieldsSerializer(data=request.data, many=many)

Categories