How do add non-model fields to DRF's serializer response dict? - python

if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
How do add one extra field (non-model field) to serializer.data dict ? I would like to add ("status" : "available") before sending the response. I tried this -
if serializer.is_valid():
serializer.save()
## This one doesn't work as serializer.data is of the type 'ReturnDict'
serializer.data.append(("status","available"))
## OR
serializer.data["status"] = "available"
return Response(serializer.data, status=status.HTTP_201_CREATED)
I also want to modify serializer.data's field names before sending the response. Is there any way to do this?

You can add an extra field to the serializer like this:
class MySerializer(serializers.ModelSerializer)
status = serializers.SerializerMethodField('get_status')
class Meta:
model = MyModel
read_only_fields = ('status',)
def get_status(self, obj):
""" Get the current objects status """
if obj.available == 1:
return 'available'
else:
return 'not available'
One way to change the field names would be to use a middleware class to format the response. I would do this if I would have to format the response for the whole API.

Related

Getting error in .is_valid() method in DRF

I am tyring to create a User Registration API, but I am getting an error when using a POST request in the following view:
#action(methods=['GET', 'POST'], detail=False, permission_classes=[AllowAny])
def users(self, request):
if request.method == 'GET':
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)
elif request.method == 'POST':
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
if serializer.available():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The exception returned is the following:
Cannot call `.is_valid()` as no `data=` keyword argument was passed when
instantiating the serializer instance.
You pass an object or a queryset to a serializer when you want to serialize it, so it's fine in your GET method.
However, when you want to create an object using a serializer, you have to pass the data parameter before calling .is_valid(), and only then you can create your new objects withsave(). This is because the serializer needs to validate the data given to him before he does any save.
What you should do is the following:
[...]
elif request.method == 'POST':
serializer = UserSerializer(data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

DRF Response isn't working with class based views in Django

I am converting my function-based views to class-based views. Following the official documentation, I was getting errors using the Response imported from rest_framework. When using HttpResponse it's working fine. The error I am getting with Response is:
.accepted_renderer not set on Response
This is my view:
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
What could be the reason for this?
serializer:
class Meta:
model = User
fields = '__all__'
instead of this:
return Response(serializer.data)
try this:
return Response(serializer.data, status=status.HTTP_200_OK) #Status is depends on your code
I think your serializer is not saved so that's why you are getting that error
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Final thing try this.

Add extra parameter to serializer.data

I get three fields from rest api i.e name , phone number and email id in serializer but i want to store the datetime as well when i receive the api . in database i have created 4 columns i.e name , phone number, email id , created data .
How can i add current date timeparameter to my serializer.data before saving it to posgtresql database table "table_userdetails". please help me with the answers because i am new to it
Below is the code for views.py
#api_view(['GET', 'POST'])
def UserInfo(request):
if request.method == 'GET':
snippets = UserDetails.objects.all()
serializer=UserDetailsSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method =='POST':
context=datetime.datetime.now()
serializer = UserDetailsSerializer(data=request.data)
print("serializer",serializer)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Below is the code for Models.py
class UserDetails(models.Model):
name=models.CharField(max_length=255)
mobile_no=models.CharField(max_length=255)
email_id=models.CharField(max_length=255)
user_id=models.IntegerField(primary_key=True)
created_date=models.CharField(max_length=255)
class Meta:
db_table=table_userdetails
def __str__(self):
return self.response
Below is my code for serializer.py
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model=UserDetails
fields='__all__'
I have added the context part in post method but how to add in serializer.data .please help me with the solution and what am i doing wrong here .any kind of help is appreciated .
Thank you
Try this:
#api_view(['GET', 'POST'])
def UserInfo(request):
if request.method == 'GET':
snippets = UserDetails.objects.all()
serializer=UserDetailsSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method =='POST':
serializer = UserDetailsSerializer(data=request.data)
print("serializer",serializer)
data = serializer.data #fetch the serializer data
#add current time as key-value pair in data as serializer.data returns a dictionary
data['current_time'] = datetime.datetime.now()
if serializer.is_valid():
serializer.save()
#return the data object/dictionary
return Response(data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You don't need to set now to time directly as you could use auto_add_now on DateTimeField/DateField model field
There is no sense to keep date in CharField
For further reference you could update() your request.data dictionary with additional attribute as just setting new variable called context does nothing
You can pass extra parameters to the serializers save method.
serializer.save(created_at=datetime.datetime.now())
Doing so you will have to take care of validating the data manually, as the serializer will directly save this to the database.
As per your requirement, for created_at field, you can use the auto_now_add=True attribute in your model field, this will automatically assign the current time to the created_at field. only during the time of creation. Similary for a updated_at field you can use auto_now=True.
Now if you want to pass extra context to the serializer, you can do so by overriding the __init__ method.
class UserDetailsSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
time = kwargs.pop('time')
super().__init__(*args, **kwargs)
self.time = time
class Meta:
model=UserDetails
fields='__all__'
Then while initializing the serializer you can pass the value
serializer = UserDetailsSerializer(data=request.data, time=datetime.datetime.now())
After that you can use this in the serializer methods. For your requirement, setting the auto_now_add attribute will be the best solution.
Edit: If you are using USE_TZ=True in your settings.py you should be using timezone.now()
from django.utils import timezone.now()
serializer.save(created_at=timezone.now())
Django uses UTC time saved to the database for a timezone aware object, which is converted to the localtime of the user as per the settings.
Refer to docs for more information

How to implement validation error messages in django rest

I am just starting to learn python and django rest framework. I am creating the sample apis in django. I am having a problem where i need to set the validation error messages.
For example email is requied or email already exists
Here is my code:
Serializer Class:
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
#fields = ['id','title','author','date']
fields = '__all__'
views.py
def post(self,request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.error,status = status.HTTP_400_BAD_REQUEST)
Model:
class Article(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
date = models.DateTimeField(auto_now_add = True)
def _str_(self):
#self.fields['email'].required = False
return self.title
Error is:
'ArticleSerializer' object has no attribute 'error'
You don't need to handle the validation manually:
def post(self, request):
serializer = ArticleSerializer(data=request.data)
serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
This will raise an Exception, but the exception will be handled by DRF (as long as you're using it correctly) and will return a 400 status code with the error messages as expected.
Also, this view does more or less exactly the same as a CreateAPIView, you'd probably be better off using that than reinventing the wheel.
Try using serializer.errors (plural) instead of serializer.error.
That will avoid the error you're getting and it will return the HTTP error you're expecting.

Django REST Framework ModelSerializer - Object not saved

I've a model, and one of it's field refers to an overrided User instance (changed in Django settings).
When I'm performing a POST from my client, the route ends up here at the create method:
class CatView(ModelViewSet):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
serializer_class = CatListSerializer
def get_queryset(self):
return Cat.objects.filter(owner=self.request.user).order_by('id')
'''
def list(self, request, format=None):
serializer = CatGetSerializer(Cat.objects.filter(owner=request.user), context={'request': request}, many=True)
return Response(serializer.data)
'''
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def create(self, request, *args, **kwargs):
serializer = CatPutSerializer(data=request.data)
if serializer.is_valid():
serializer.create(serializer.data)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
When using a PUT to do a partial update on my model, it works fine. But creating one just doesn't work. I manually inject the user instance into the serializer and asks it to create the object. Then... nothing. No exception raises, it returns the proper data, but the object is not in my database, not being saved.
What's the issue here?
EDIT:
When I'm adding the owner field to the CatPutSerializer, it opens security issues since I don't know how to prevent this to be changed as I don't want the client to send me which user to assign. And when I'm duplicating the serializer to be used on POST only requests, it says it misses the owner field...
Here's the CatPutSerializer:
class CatPutSerializer(serializers.ModelSerializer):
class Meta:
model = Cat
fields = ('name', 'weight', 'sterilized', 'image', 'tag', 'dob', 'race', 'gender')
UPDATE:
As suggested, I'm now doing as follows :
def create(self, request, *args, **kwargs):
pdb.set_trace()
serializer = CatPutSerializer(data=request.data)
if serializer.is_valid():
serializer.save(owner=self.request.user)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
Though removed the perform_create overriding.
SOLUTION:
After further investigation, it doesn't seem related to drf but to Django / PostgreSQL itself, so I checked to Django model save method, and it seems that my custom image processing prevented from new objects to be created... Changed it and now works.
You seem to be overriding both create and perform_create. If you look at the code for CreateModelMixin which the ModelViewSet inherits from you will notice that create calls perform_create, which calls serializer.save(). You don't call perform_create in your create method; you seem to be calling serializer.create(...). If you are overriding create, simply do this:
def create(self, request, *args, **kwargs):
serializer = CatPutSerializer(data=request.data)
if serializer.is_valid():
serializer.save(owner=self.request.user)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

Categories

Resources