How to get the id attribute when I'm inside the perform_validation method of the serializer - django-rest-framework

When I'm inside the perform_validation method of a given serializer I need access to the "id" of the model I've requested (at my RetrieveUpdateDestroy endpoint)
def perform_validation(self, attrs):
name = attrs['name']
guid = attrs['id'] #broken :(
What is the best way to get this when I'm inside the validation method?

Use the serializer context, that's provided by the generic view.
See here for what context is populated by default: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/generics.py#L83
You should be able to do something like this: self.context['view'].kwargs['pk']

Related

is creating serializer mandatory for each View in django rest frame work

I am working on app where I didnt use serializer in one view I just want to ask am I doing something wrong.
I am getting a committee id in the url and in the body I am getting the usr_id whos status I want to change if someone sends a post request to this end point.
This is my url
path('committee/<int:id>/accept-request/', RequestAcceptView.as_view(), name="accept-request"),
this is my view.py
class RequestAcceptView(APIView):
def post(self, request, id):
user_id = request.data['user']
print("iddddd",user_id )
try :
approve_request = Member.objects.filter(
Q (com=Committee.objects.get(id=id))
).update(mem_status="fully_approved")
return Response(SuccessResponse(msg="Request accepted"))
except:
return Response(ErrorResponse(msg="invalid))
I want to know I am not using serializer here for any purpose, is it fine? should I remove serializer file?
No, it is not when you are using ApiView. Generic and ModelView requires them. You should also think about if you want to have auto generated documentation you need the serializer and it will also perform validation (because you are not using the id field, but request.data.
If request.data and I'd are the same, then you might want to delete the serializer

Django REST Framework: "NoneType object is not iterable" error when trying to use serializer.data construct from within Serializer Method Field?

I am using a model that consists of many fields. There is one field that is a property, and it returns an instance of a model. Something like the following:
class A(Model):
#property
def last_obj(self):
# Returns an object
The issue I'm having is that this property can return 2 different Model types. It can either return an object of type one, or an object of type two. This creates complications in the serializer. I have a serializer that consists of nested serializers. The two objects are similar enough that one serializer can be used over the other, but then the fields unique to them are not serialized.
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
last_obj = SerializerOne() #This works, but not viable because of what I stated above
So my solution to be able to dynamically call the correct serializer, was to conditionally serialize the property within a serializer method field:
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
def get_last_obj(self, instance):
if (isinstance(instance.last_obj, models.one)):
return self.SerializerOne(instance.last_obj).data
else:
return self.SerializerTwo(instance.last_obj).data
last_obj = SerializerMethodField() #Does not work
However, this solution creates the error "NoneType Object is not iterable" and it happens at
super(ReturnDict, self).__init__(*args, **kwargs) in rest_framework/utils/serializers_helpers.py in init which causes the error at return ReturnDict(ret, serializer=self) in rest_framework/serializers.py in data
I do not understand why calling a nested serializer like obj = Serializer() works, but calling the serializer explicitly like obj = Serializer(instance).data does not work in this situation. Can anyone figure out what I have been doing wrong? Thank you.
I have found out from here that when working with hyperlinked relations (which in my case was the CustomSerializer that SerializerOne and SerializerTwo were inheriting from), you must pass the request object through context. The reason why obj = Serializer() works, but obj = Serializer(instance).data does not work is that in the former, the request object is automatically added through context through DRF. While in the latter, it is being explicitly called so you must pass context with the request object manually. So for me to get it working, I did:
return self.SerializerOne(instance.last_obj, context={'request': self.context['request']}).data
inside the serializer method field.

DRF: What's the difference between Field and Serializer

We can use serializer as a field inside another serializer..
Wonder why there's a Field class and Serializer class in DRF?
class CommentSerializer(serializers.Serializer):
user = UserSerializer()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
example serializer is taken from the doc https://www.django-rest-framework.org/api-guide/serializers/
As you can see, UserSerializer is much like a Field .
I'm just curious why they have serializer and field class separately..
Serilaizer is infact a field in DRF. Serializers can be nested and that is why it can be used as a field in other serializers. And yes, if you check the source code, the BaseSerializer is a subclass of Field as the serializer is just a special case of a field.
In my opinion:
In django rest framwork, you can think Serializer like a mask. It cover your origin data and change it to anything you want. Like format your json data , or validate your input data have correct format or not.
In your example,
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Comment have 2 direct field type CharField and DateTimeField.
user = UserSerializer()
Comment have field type is UserSerializer. This is different Serializer, and django know your CommentSerializer will have relationship with UserSerializer. And anything define in UserSerializer will use in here, for format json output or validate. And with define this nested objects, your output json will have more data like
'user': {'email': 'foobar', 'username': 'doe'}
And if you want create Comment with one user, you must pass all validate define in UserSerializer.
Conclude: in this example
Field class use for direct field.
Serializer class for relationship with other class

Custom serializer with django 2

I would like to create a serializer that given an input JSON object select certain attributes.
There is an legacy system that sends an object, for example:
{
"a": {
"b": "test"
}
}
I need to keep certain properties of the object. For example a.b. I have created the following serializer, but it is not working:
class CustomSerializer(serializers.Serializer):
b = serializers.CharField(source='a.b', required=True)
Does anyone know what is failing?
Thanks!
You need to implement nested serializer. For your example, you can try something like this :
class BSerializer(serializers.Serializer):
b = serializers.CharField()
class ASerializer(serializers.Serializer):
a = BSerializer()
Note : this is just an example of nested serializer. You might need to add few things to make it work properly for you (adding Meta, other fields if needed, use it in view etc. )

Model Serializer : choose which fields to display and add custom fields

Let's say I have this simple model :
class BlogPost(models.Model):
author = models.ForeignKey(MyUser)
body = models.TextField()
title = models.CharField(max_length=64)
urlid = models.CharField(max_length=32)
private_data = models.CharField(max_length=64)
private_data contains data that I do not want to expose to the API (!). I'm using a ModelSerializer :
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
def __init__(self, *args, **kwargs):
# Don't pass the 'request' arg up to the superclass
request = kwargs.pop('request', None)
# Instatiate the superclass normally
super(ModelSerializer, self).__init__(*args, **kwargs)
self.request = request
def absolute_url(self, blogpost):
return blogpost.get_absolute_url(self.request)
The absolute_url method needs the request to determine the domain name (dev or prod for example) and if it was made in http or https.
I want to specify which fields in the model are going to get returned by the serializer (not expose private_data for example). Simple enough:
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('author', 'body', 'title', 'urlid',)
# The same jazz after that
All right, it works. Now I also want to return absoluteUrl:
class BlogPostSerializer(serializers.ModelSerializer):
absoluteUrl = serializers.SerializerMethodField('absolute_url')
class Meta:
model = BlogPost
fields = ('author', 'body', 'title', 'urlid',)
# The same jazz after that
Well, without surprises, this returns only the fields I specified, without the absoluteUrl. How can I return only certain fields of the model AND the absoluteUrl, calculated from the serializer?
If I don't specify fields I do get the absoluteUrl, but with all the model's fields (including private_data). If I add 'absoluteUrl' to fields I get an error because blogpost.absoluteUrl doesn't exist (no surprises there). I don't think I could use this method http://django-rest-framework.org/api-guide/serializers.html#specifying-fields-explicitly because I need the request to obtain the absoluteUrl (or can I specify arguments to the model's method ?)
If I don't specify fields I do get the absoluteUrl, but with all the model's fields (including private_data). If I add 'absoluteUrl' to fields I get an error because blogpost.absoluteUrl doesn't exist (no surprises there).
You should just be adding 'absoluteUrl' to the fields tuple, and it should work just fine - so what error are you seeing?
The absolute_url method needs the request to determine the domain name (dev or prod for example) and if it was made in http or https.
Note that you can also pass through context to the serializer without modfiying the __init__, just pass a context={'request': request} when instantiating the serializer. The default set of generic views do this for you, so you can access self.context['request'] in any of the serializer methods. (Note that this is how hyperlinked relationships are able to return fully qualified URLs)

Resources