Django Rest Framework KeyError 'request' - django

i have my serializer like this
class PublicacionSerializer(serializers.ModelSerializer):
usuario = UserSerializer2()
likeado = serializers.SerializerMethodField()
class Meta:
model = Publicacion
fields = ('id','usuario', 'likeado')
def get_likeado(self, obj):
user = self.context['request'].user
try:
like = Like.objects.get(publicacion=obj, usuario=user)
return like.id
except Like.DoesNotExist:
return False
so i use that seriaizer in another one:
class EstadoSerializer(serializers.ModelSerializer):
publicacion = PublicacionSerializer(read_only=True)
in views.py i have
class ModificarEstadoMixin(object):
queryset = Estado.objects.all()
serializer_class = EstadoSerializer
class ModificarEstadoDetail(ModificarEstadoMixin, RetrieveUpdateAPIView):
permission_classes = (permissions.IsAuthenticated,
CanModifEstado,)
pass
when i access to the url for know if an user has liked to a post i got a KeyError 'request' in code line
user = self.context['request'].user
anyone knows how to solve it?

When you call that serializer, you have to pass context from view like
MySerializer(context={'request': request})

Related

Querying and Filtering related models in DRF

I have Contact model to list the followers of an User object, I try to filter the contacts of a User but I still could not manage get a correct queryset. My Contact model is simple with two ForeignKey:
class Contact(models.Model):
user_from = models.ForeignKey(User,related_name='rel_from_set', on_delete=models.CASCADE,)
user_to = models.ForeignKey(User,related_name='rel_to_set', on_delete=models.CASCADE,)
def __str__(self):
return '{} follow {}'.format(self.user_from, self.user_to)
I have created serializers for User and Contact:
##Contact Serializer
class ContactsSerializer(serializers.ModelSerializer):
user_from = serializers.StringRelatedField(read_only=True)
user_to = serializers.StringRelatedField(read_only=True)
class Meta:
model = Contact
fields = ["user_from", "user_to"]
##UserSerializer
class UserInformationSerializer(serializers.ModelSerializer):
followers = ContactsSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['first_name', 'last_name', 'followers']
​
And try to make a query through views:
class FollowerListView(APIView):
queryset = Contact.objects.all()
serializer_class = ContactsSerializer
lookup_field = "username"
def get(self, request, format=None, slug=None):
kwarg_username = self.kwargs.get("slug")
user = User.objects.filter(is_active=1).filter(username=kwarg_username)
print(user.username)
contacts = Contact.objects.filter(user_to=user.id)
serializer = ContactsSerializer(contacts)
return Response(serializer.data)
Now I get error message:
AttributeError at /api/member/ytsejam/followers/
'QuerySet' object has no attribute 'username'
print(user.username)
If i try print(user) I can see the user an Object.
Can you guide me how to correct?
Thanks
filter will always return a queryset. If you expect to retrieve one single item, use get.
So that it looks like that:
def get(self, request, format=None, slug=None):
kwarg_username = self.kwargs.get("slug")
user = User.objects.filter(is_active=1).get(username=kwarg_username)
print(user.username)
contacts = Contact.objects.filter(user_to=user.id)
serializer = ContactsSerializer(contacts)
return Response(serializer.data)
You could, of course, do this on one take:
User.objects.get(is_active=1, username=kwarg_username)
But beware, if there are two rows in your model that would satisfy this call, Django will throw an error. Best make sure that the username has a unique constraint.

KeyError 'request' in DRF

I am checking in serializer if product exists in cart or not and I am using this
class ProductSerializer(serializers.ModelSerializer):
in_cart = serializers.SerializerMethodField()
class Meta:
model = Product
fields = ['id', 'in_cart']
def get_in_cart(self, obj):
user = self.context['request'].user
if user.is_authenticated:
added_to_cart = Cart.objects.filter(user=user, product_id=obj.id).exists()
return added_to_cart
else:
return False
It works fine but I cannot add product to the cart because of that request
my cart model like this
class Cart(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
quantity = models.PositiveIntegerField()
def __str__(self):
return f'{self.user} cart item'
class ItemsListView(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
When I post product id to add cart it throws this error
user = self.context['request'].user KeyError: 'request'
I need to make both work but adding item to cart is being problem.
How can I solve this? Thank you beforehand!
You need to pass the request to the context before usage. So the calling of serializer should look like this:
ProductSerializer(product, context={'request': request})
With ListAPIView class you don't even need this, because by default it would be available in the serializer due to the default implementation of get_serializer_context method:
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
Although you could override it if needed. Also, take a note that serializer_class accepts a callable object, it should be serializer_class = ProductSerializer.
I have had the same problem while using nested serializers. As mentioned above, you just can pass self.context['request'] to the context of nested serializer:
'author': GETUserSerializer(
recipe.author,
context={'request': self.context['request']}
).data,

DRF - SerializerMethodField

I've an API view as below:-
class ProfileAPI(generics.RetrieveAPIView):
serializer_class = ProfileSerializer
def get_object(self):
try:
return Profile.objects.get(user=self.request.user)
except:
return None
# I don't raise NotFound here for a reason.
# I don't want a 404 response here, but a custom HTML response, explained below.
class ProfileSerializer(serializers.ModelSerializer):
html = serializers.SerializerMethodField()
def get_html(self, obj):
# some custom HTML response based on whether the user obj is `None` or not.
if not obj:
return NOT_LOGGED_IN_HTML
return CUSTOM_HTML
class Meta(object):
model = Profile
fields = ('html',)
Now when the user is logged-in, I get the html key in the response. However, when the user is None (logged-out), I get an empty response. Why? and how can I rectify it?
As far as I can understand from implementation of retrieve and data method, you need to pass an instance of Profile to populate data. I would approach like this:
class ProfileAPI(generics.RetrieveAPIView):
serializer_class = ProfileSerializer
def get_object(self):
try:
return Profile.objects.get(user=self.request.user)
except:
return Profile() # empty object instance
class ProfileSerializer(serializers.ModelSerializer):
html = serializers.SerializerMethodField()
def get_html(self, obj):
if obj and obj.pk:
return CUSTOM_HTML
return NOT_LOGGED_IN_HTML
class Meta(object):
model = Profile
fields = ('html',)

Django REST Framework custom permission class not working

In my attempt to troubleshoot an issue, I'm trying to force my custom permission class to return False and it's not. I'm still able to perform a successful GET and POST request via the DateListViewSet class below. I can't figure out why my custom permission class (IsUser) below isn't working Below is my custom permission class, view class and serializer. Please assist
Custom Permission Class
class IsUser(permissions.BasePermission):
def has_permissions(self, request, view):
return False
Mixin and View Class
class DateListMixin(object):
serializer_class = SimpleDateSerializer
permission_classes = (permissions.IsAuthenticated, IsUser)
class DateListViewSet(DateListMixin, generics.BulkModelViewSet):
def get_queryset(self):
num = self.kwargs['rm']
num2 = self.kwargs['id']
r1 = Room.objects.get(pk=num)
s1 = Schedule.objects.get(pk=num2)
u = self.request.user.pk
usr = User.objects.get(pk=u)
if(s1.user.username == usr.username):
queryset = r1.transactiondatetime_set.all()
return queryset
else: raise Http404("User does not exist")
Serializer class
class SimpleDateSerializer(BulkSerializerMixin, ModelSerializer, serializers.Serializer):
start_dt = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
class Meta(object):
model = TransactionDateTime
list_serializer_class = BulkListSerializer
fields = ('pk', 'start_dt', 'room')
I figured out it. I should have typed permission instead of permissions

Pass request context to serializer from Viewset in Django Rest Framework

I have a case where the values for a serializer field depend on the identity of the currently logged in user. I have seen how to add the user to the context when initializing a serializer, but I am not sure how to do this when using a ViewSet, as you only supply the serializer class and not the actual serializer instance.
Basically I would like to know how to go from:
class myModelViewSet(ModelViewSet):
queryset = myModel.objects.all()
permission_classes = [DjangoModelPermissions]
serializer_class = myModelSerializer
to:
class myModelSerializer(serializers.ModelSerializer):
uploaded_by = serializers.SerializerMethodField()
special_field = serializers.SerializerMethodField()
class Meta:
model = myModel
def get_special_field(self, obj):
if self.context['request'].user.has_perm('something.add_something'):
return something
Sorry if it wasn't clear, from the DOCs:
Adding Extra Context
Which says to do
serializer = AccountSerializer(account, context={'request': request})
serializer.data
But I am not sure how to do that automatically from the viewset, as I only can change the serializer class, and not the serializer instance itself.
GenericViewSet has the get_serializer_context method which will let you update context:
class MyModelViewSet(ModelViewSet):
queryset = MyModel.objects.all()
permission_classes = [DjangoModelPermissions]
serializer_class = MyModelSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
context.update({"request": self.request})
return context
For Python 2.7, use context = super(MyModelViewSet, self).get_serializer_context()
For Function based views you can pass request or user as follows:
serializer = ProductSerializer(context = {"request": request}, data=request.data)
Your Serializer may look like:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id"]
def create(self, validated_data):
user = self.context["request"].user
print(f"User is: {user}")
Feel free to inform if there is any better way to do this.
just use get_serializer() in your viewsets
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
Return parent context in overrided function get_serializer_context will make it easy to access request and its data.
class myModelViewSet(ModelViewSet):
queryset = myModel.objects.all()
permission_classes = [DjangoModelPermissions]
serializer_class = myModelSerializer
def get_serializer_context(self):
"""
pass request attribute to serializer
"""
context = super(myModelViewSet, self).get_serializer_context()
return context
This is very stable as every time we request viewset, it returns context as well.
the values for a serializer field depend on the identity of the currently logged in user
This is how I handle such cases in my ModelViewSet:
def perform_create(self, serializer):
user = self.request.user
if user.username == 'myuser':
serializer.data['myfield'] = 'something'
serializer.save()
Simply add this 2 line method in your class and you are good to go.
def get_serializer_context(self):
return {'request': self.request}
since the posted answers had partial correctness, summarizing here in the interest of completeness.
override get_serializer_context..AND
use get_serializer in your views instead of manually calling the serializer