Django Rest required parameters in URL - python

I'm using django rest framework.
Here is my code:
urls.py:
urlpatterns = [
url(r'^users/show', UserShow.as_view()),
]
view.py:
class UserShow(ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def get_queryset(self):
queryset = User.objects.all()
username = self.request.query_params.get('username', None)
user_id = self.request.query_params.get('user_id', None)
if username is not None:
queryset = queryset.filter(username=username)
if user_id is not None:
queryset = queryset.filter(pk=user_id)
return queryset
I want to get values from url like this:
/users/show?user_id=1 or /users/show?username=mike.
Either an user_id or username must be required parameter. How can I control it in class based views?
With my code if I'm sending the request with wrong parameter name /users/show?user111name=mike or simple /users/show the view of course response me with queryset = User.objects.all() and lists all the users. I don't need that. I need if required parameters are None response with 404.
I can get needed result with function based view:
#api_view(['GET'])
def users(request):
if request.method == 'GET':
queryset = User.objects.all()
username = request.GET.get('username', None)
user_id = request.GET.get('user_id', None)
if username is not None:
queryset = queryset.filter(username=username)
elif user_id is not None:
queryset = queryset.filter(pk=user_id)
else:
return Response({"status": "required field not found."},
status=status.HTTP_404_NOT_FOUND)
if not queryset.exists():
return Response({"status": "not found."},
status=status.HTTP_404_NOT_FOUND)
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
But how can I do it with generic class based views?

class UserIdRetrieve(RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserUsernameRetrieve(UserIdRetrieve):
lookup_field = 'username'
and in urls:
urlpatterns = [
url(r'^users/(?P<pk>\d+)/', UserIdRetrieve.as_view()),
url(r'^users/by-username/(?P<username>\w+)/', UserUsernameRetrieve.as_view())
]
if your url structure is a must, small change to above:
class UserIdRetrieve(RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
if 'username' in self.request.query_params:
filter_kwargs = {'username': self.request.query_params['username']}
elif 'user_id' in self.request.query_params:
filter_kwargs = {'id': self.request.query_params['user_id']}
else:
raise Http404('Missing required parameters')
obj = get_object_or_404(queryset, **filter_kwargs)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
and in urls:
urlpatterns = [
url(r'^users/show', UserRetrieve.as_view())
]

class UserShow(ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def filter_queryset(self, queryset):
username = self.request.query_params.get('username', None)
user_id = self.request.query_params.get('user_id', None)
if username is not None:
queryset = queryset.filter(username=username)
if user_id is not None:
queryset = queryset.filter(pk=user_id)
return queryset
def list(self,request,*args,**kwargs):
username = self.request.query_params.get('username', None)
user_id = self.request.query_params.get('user_id', None)
if not (username or user_id):
return Response({"status": "Required field not found."},
status=status.HTTP_404_NOT_FOUND)
return super(UserShow, self).list(request,*args,**kwargs)

Related

Django REST how to not apply default permission for get request

I don't want to apply my permission_classes for get request. I tried #permission_classes([AllowAny]) but didn't work.
Here is my my code:
class BlogViewSet(viewsets.ModelViewSet):
queryset = Blog.objects.all()
serializer_class = BlogSerializer
pagination_class = BlogPagination
lookup_field = 'blog_slug'
permission_classes = [IsOwnerOrReadOnly & IsAuthorGroup]
#permission_classes([AllowAny])
def list(self, request):
if request.method == "GET":
blog = Blog.objects.all().order_by("id")
serializer = BlogSerializer(blog, many=True)
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
I also tried this and getting local variable 'permission_classes' referenced before assignment for post put patch and delete request
class BlogViewSet(viewsets.ModelViewSet):
queryset = Blog.objects.all()
serializer_class = BlogSerializer
pagination_class = BlogPagination
lookup_field = 'blog_slug'
permission_classes = [IsOwnerOrReadOnly & IsAuthorGroup]
def get_permissions(self):
if self.action == "list":
permission_classes = [
AllowAny,
]
return [permission() for permission in permission_classes]
you can simply pass a permission class like this.
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly, AllowAny, IsAdminUser
class BlogViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly,IsAuthenticated ]
queryset = Blog.objects.all()
serializer_class = BlogSerializer
pagination_class = BlogPagination
lookup_field = 'blog_slug'
And you can write your own permission classes to pass it. and user will access this view only if they will have all permissions.
Hope this will help you :)

Expected view to be called with URL keyword "pk" added "pk" to urls.py

Getting the error:
AssertionError at /update_profile/ Expected view UpdateProfileView to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_fieldattribute on the view correctly.
when I try to update a user profile.
Here is my urls.py
path('update_profile/', views.UpdateProfileView.as_view(), name='update_profile'),
My UpdateProfileView:
class UpdateProfileView(generics.UpdateAPIView):
# permission_classes = [permissions.IsAuthenticated]
# authentication_classes = (TokenAuthentication,)
# lookup_field = 'username'
queryset = User.objects.all()
serializer_class = UpdateUserSerializer
#action(detail=True, methods=['put'])
def perform_update(self, serializer):
serializer.save(user=self.request.user.id)
UpdateUserSerializer
class UpdateUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
class Meta:
model = User
#add 'city', 'country', 'bio'
fields = ['username', 'email', 'password', 'first_name', 'last_name']
def validate_email(self, value):
user = self.context['request'].user
if User.objects.exclude(pk=user.pk).filter(email=value).exists():
raise serializers.ValidationError({"email": "This email is already in use."})
return value
def validate_username(self, value):
user = self.context['request'].user
if User.objects.exclude(pk=user.pk).filter(username=value).exists():
raise serializers.ValidationError({"username": "This username is already in use."})
return value
def update(self, instance, validated_data):
user = self.context['request'].user
if user.pk != instance.pk:
raise serializers.ValidationError({"authorize": "You don't have permission for this user."})
instance.first_name = validated_data['first_name']
instance.last_name = validated_data['last_name']
instance.email = validated_data['email']
instance.username = validated_data['username']
# instance.profile.city = validated_data['city']
# instance.profile.country = validated_data['country']
# instance.profile.bio = validated_data['bio']
instance.save()
return instance
I added the URL argument pk to my urls.py
path('update_profile/<int:pk>', views.UpdateProfileView.as_view(), name='update_profile')
This returns the error page not found at update_profile

Django Viewset retrieve not working with Default Router

I am trying to access a specific comment using the retrieve method in Django View sets. I am using a default router in order to route my urls. I am able to list all comments at api/posts/, but am unable to get a single comment at api/posts/1. I am getting a type error: Field.init() got an unexpected keyword argument 'pk' when trying to access the URL. Any ideas as to why?
urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from posts.views import PostsViewSet, CommentsViewSet
router = DefaultRouter()
router.register(r"comments", CommentsViewSet, basename='comments')
urlpatterns = [
path("", include(router.urls)),
]
views.py
class CommentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(label='commentID')
comment = serializer.CharField()
created_at = serializers.DateTimeField(required=False)
updated_at = serializers.DateTimeField(required=False)
posts = serializers.SerializerMethodField()
class Meta:
model = Comments
# fields = "__all__"
fields = ['id', 'comment','created_at', 'updated_at', 'posts']
def to_representation(self, instance: Comment) -> dict:
'''Pass for now'''
ret = super().to_representation(instance)
return ret
def get_queryset(self) -> QuerySet:
qs = Comment.objects.all()
return qs
def create(self, validated_data: dict) -> Comment:
return Comment.objects.create(**validated_data)
def update(self, instance: Comment, validated_data: dict) -> Comment:
'''Pass post-validation errors silently'''
for field in validated_data:
setattr(instance, field, validated_data.get(
field, getattr(instance, field)))
instance.save()
return instance
class CommentViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Comment.objects.all()
result = CommentSerializer(queryset, many=True)
if result:
return Response(result.data)
else:
return Response(data=result.data, status=200)
def retrieve(self, request, pk=None):
queryset = Comment.objects.all()
condition = get_object_or_404(queryset, pk=pk)
result = CommentSerializer(queryset, pk=pk)
print(result)
return Response(result.data)
First you are not using the condition object in serializer.
Second drf default pk field name is id not pk and no need to pass pk during serialization process.Below is updated retrieve method kindly check the same.
def retrieve(self, request, pk=None):
# queryset = Comment.objects.all() #no need as you are using get_object_or_404 to fetch single object
condition = get_object_or_404(Comment, id=pk) #replaced pk by id here
result = StudentSerializer(condition) #used condition in serializer
print(result)
return Response(result.data)

DetailApiViewset with classed based api view

I am trying to create detail API viewset but for now, I am getting page not found whenever I go to my "matches/api/matches/1/". ListCreateAPIView works fine.
Here are my viewsets:
class MatchViewset(viewsets.ViewSetMixin, generics.ListCreateAPIView):
serializer_class = MatchSerializer
def get_queryset(self):
header_token = self.request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
if header_token is not None:
try:
token = sub('Token ', '', self.request.META.get('HTTP_AUTHORIZATION', None))
token_obj = Token.objects.get(key=token)
self.request.user = token_obj.user
except Token.DoesNotExist:
pass
print(self.request.user)
return Match.objects.filter(creator=self.request.user)
class MatchDetailViewset(generics.RetrieveUpdateDestroyAPIView):
queryset = Match.objects.all()
serializer_class = MatchSerializer
and routers:
router = routers.DefaultRouter()
router.register(r'matches', matches_views.MatchViewset, base_name="matches")
and urls:
app_name = 'matches'
urlpatterns = [
path('api/', include(router.urls)),
]
According to the docs here: Routers usage
after the router.register(r'matches', matches_views.MatchViewset, base_name="matches") you must give
urlpatterns = router.urls
I guess this would solve the issue
The MatchDetailViewset is not registered in urlpatterns, that's why you're getting a 404 error
It seems like DRF router not support registering multiple viewset with the same prefix.
So maybe you should try something like this:
class MatchViewSet(viewsets.ModelViewSet):
serializer_class = MatchSerializer
queryset = Match.objects.all()
def list(self, request, *args, **kwargs):
header_token = self.request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
if header_token is not None:
try:
token = sub('Token ', '', self.request.META.get('HTTP_AUTHORIZATION', None))
token_obj = Token.objects.get(key=token)
self.request.user = token_obj.user
except Token.DoesNotExist:
pass
print(self.request.user)
# customize the queryset for listview
queryset = self.get_queryset().filter(creator=self.request.user)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

ContentNotRenderedError : The response content must be rendered before it can be iterated over

while returning custom response I having render error.
I am using django 1.8 and DRF 2.4.8
view is
class UserList(generics.ListAPIView):
# queryset = User.objects.all()
serializer_class = UserSerializer
def get_queryset(self):
is_staff = self.request.GET.get('is_staff', None)
is_superuser = self.request.GET.get('is_superuser', None)
is_admin = self.request.GET.get('is_admin', None)
company = self.request.GET.get('company', None)
userdetails = UserData(self.request.user).userdetails()
print(userdetails.get('usertype'))
query1 = query = Q()
if is_staff:
query1.add(Q(is_staff=is_staff), Q.AND)
if is_superuser:
query1.add(Q(is_superuser=is_superuser), Q.AND)
if is_admin:
query = Q(is_admin=is_admin)
if company:
query.add(Q(company__name=company), Q.AND)
if userdetails.get('usertype') is 'superuser':
if is_admin or company:
profile_list = Profile.objects.filter(query).values_list('user_id', flat=True)
user_list = User.objects.filter(id__in=profile_list)
return user_list.filter(query1)
else:
return User.objects.filter(query1)
elif userdetails.get('usertype') in ['staff', 'admin']:
up = Profile.objects.get(user=self.request.user)
profile_list = Profile.objects.filter(company=up.company) # .values_list('user_id', flat=True)
user_list = profile_list.filter(query).values_list('user_id', flat=True) if is_admin or company else \
profile_list.values_list('user_id', flat=True)
final_list = User.objects.filter(id__in=user_list)
return final_list.filter(query1)
else:
return Response(data={"error": ["You don't have enough permission."]}, status=status.HTTP_400_BAD_REQUEST)
while returning response from last else part I am getting error?
What is the solution? what is wrong in Response()?
get_queryset should return a QuerySet, not a Response.
Yet you can still act as if it isn't valid by raising an validation exception:
from rest_framework.exceptions import ValidationError
def get_queryset(self):
...
else:
raise ValidationError({"error": ["You don't have enough permission."]})

Categories