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