Django REST how to not apply default permission for get request - python

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 :)

Related

How to write permissions in a viewset with conditional statements in DRF?

I have a viewset written in DRF:
class MyViewSet(ModelViewSet):
serializer_class = MySerializer
queryset = models.MyClass.objects.all()
def get_serializer_class(self):
permission = self.request.user.permission
if permission=='owner' or permission=='admin':
return self.serializer_class
else:
return OtherSerializer
def perform_create(self, serializer):
permission = self.request.user.permission
if permission=='owner' or permission=='admin':
serializer.save()
else:
employee = models.Employee.objects.get(user=self.request.user)
serializer.save(employee=employee)
Here, I am using the following statements in both get_serializer_class and perform_create which looks like a repetitive code:
permission = self.request.user.permission
if permission=='owner' or permission=='admin':
Is there any way to write it once and then use it as a permission_class somehow?
Create a Custom Permission class
https://www.django-rest-framework.org/api-guide/permissions/#custom-permissions
from rest_framework.permissions import BasePermission, SAFE_METHODS
class CustomPermission(BasePermission):
def has_permission(self, request, view):
if request.method in SAFE_METHODS:
return True
permission = self.request.user.permission
if permission=='owner' or permission=='admin':
return True
return False
in Views.py
class MyViewSet(ModelViewSet):
serializer_class = MySerializer
queryset = models.MyClass.objects.all()
permission_classes = (CustomPermission,)

Django url dispetcher route by id

this is my
views.py
class UserAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get_object(self):
return self.request.user
and this is my url.py
path('V1/api/users/', UserAPI.as_view(), name='user'),
when i enter id,mail,username and go to
localhost/v1/api/users/1 want to open user's which id is 1.
what is best solutions ?
Your problem comes from your url settings your should add primary key to the url. Change this
path('V1/api/users/', UserAPI.as_view(), name='user')
To this
path('V1/api/users/<pk>/', UserAPI.as_view(), name='user'),
path('V1/api/users/<int:user_id>', UserAPI.as_view(), name='user'),
class UserApi(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated,]
serializer_class = UserSerializer
def get(self, request, user_id, format=None):
print(user_id) # do whatever you want with the user id

How can I access the serializer in extra actions?

I have the following two classes and I want two merge EventInvitationCreateView into EventInvitationViewSet.
However, I am struggling to bring perform_create into create_invitation as I still need to access serializer. Do you have any input on how to achieve that?
class EventInvitationCreateView(CreateAPIView):
serializer_class = InvitationSerializer
permission_classes = [RetoolPermission]
queryset = Invitation.objects.none()
def perform_create(self, serializer):
email = serializer.validated_data["email"]
event = self.request.event
invitation_exists = Invitation.objects.filter(
email__iexact=email, event=event
).exists()
if invitation_exists:
raise ValidationError("Email already exists")
serializer.save(event=event)
class EventInvitationViewSet(
mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet
):
permission_classes = [RetoolPermission]
serializer_class = InvitationSerializer
filter_backends = [filters.SearchFilter]
search_fields = ["email"]
queryset = Invitation.objects.none()
def get_queryset(self) -> QuerySet:
return self.request.event.invitations.all()
#action(detail=True, methods=["post"])
def create_invitation(self, request, pk=None):
[...perform_create]
the action is still a standard view's method, so you can use self.get_serializer()
for instance
#action(detail=True, methods=["post"])
def create_invitation(self, request, pk=None):
s = self.get_serializer(data=request.data)
....
If you need to use a "action specific serializer" you can specify the serializer class in the decorator
#action(detail=True, methods=["post"], serializer_class=MyOtherSerializer)
def create_invitation(self, request, pk=None):
s = self.get_serializer(data=request.data) # <-- instance of MyOtherSerializer
....

Why django rest api root does not list classic endpoints

I have registerd some of my endpoints through routers and written others in classic style, by classic style I mean that they are not registerd through router but directly written in path. Now problem is that api root lists only the endpoints that are registered through router and i want both to be listed.
app/views.py
from uuid import UUID
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from event.models import Event
from event.serializers import EventSerializer
from food_album.models import FoodAlbums
from food_album.serializers import FoodAlbumsSerializer
from restaurant.models import RestaurantProfile
from restaurant.serializers import RestaurantProfileSerializer
from .serializers import *
customer_favourites = {
0: [RestaurantProfile, RestaurantProfileSerializer],
1: [Event, EventSerializer],
2: [FoodAlbums, FoodAlbumsSerializer]
}
class CustomerProfileViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = CustomerProfile.objects.all()
serializer_class = CustomerProfileSerializer
lookup_field = 'public_id'
def get_serializer_class(self):
if self.action in ['create', 'update']:
return CustomerProfileCreateSerializer
return self.serializer_class
class RewardTypesViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = RewardTypes.objects.all()
serializer_class = RewardTypesSerializer
lookup_field = 'public_id'
class RewardActionsViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = RewardActions.objects.all()
serializer_class = RewardActionsSerializer
lookup_field = 'public_id'
class ReportItemViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = ReportItem.objects.all()
serializer_class = ReportItemSerializer
lookup_field = 'public_id'
class CustomerFavouritesViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
serializer_class = CustomerFavouritesSerializer
permission_classes = (IsAuthenticated,)
lookup_field = 'public_id'
def perform_create(self, serializer):
serializer.save(customer=self.request.user.customer)
def get_queryset(self):
user = self.request.user
if user.customer:
return CustomerFavourites.objects.filter(customer=user.customer)
else:
return None
class UserFeedsViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
serializer_class = RestaurantProfileSerializer
def retrieve(self, request):
r = RestaurantProfile.objects.all()
serializer = RestaurantProfileSerializer(r, many=True)
return Response(serializer.data)
class CustomerBookmarkViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
permission_classes = (IsAuthenticated,)
def list(self, request, reference_type):
bookmarks = CustomerFavourites.objects.filter(type=0,
reference_type=reference_type).values_list(
'reference_id', flat=True)
public_ids = [UUID(id_) for id_ in bookmarks]
r = customer_favourites.get(reference_type)[0].objects.filter(
public_id__in=public_ids).all()
serializer = customer_favourites.get(reference_type)[1](r, many=True)
return Response(serializer.data)
class CustomerFavouriteViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
permission_classes = (IsAuthenticated,)
def list(self, request, reference_type):
favs = CustomerFavourites.objects.filter(type=1,
reference_type=reference_type).values_list(
'reference_id', flat=True)
public_ids = [UUID(id_) for id_ in favs]
r = customer_favourites.get(reference_type)[0].objects.filter(
public_id__in=public_ids).all()
serializer = customer_favourites.get(reference_type)[1](r, many=True)
return Response(serializer.data)
app/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r'profile', views.CustomerProfileViewSet,
base_name='profile')
router.register('reward-actions', views.RewardActionsViewSet, base_name='reward_actions')
router.register('reward-types', views.RewardTypesViewSet, base_name='reward_types')
router.register('report-item', views.ReportItemViewSet, base_name='report_item')
router.register('toggle-favourites', views.CustomerFavouritesViewSet, base_name='toggle_favourites')
urlpatterns = [
path('customer/', include(router.urls)),
path('customer/user-feeds/',
views.UserFeedsViewSet.as_view({'get': 'retrieve'})),
path('customer/favourites/<int:reference_type>/',
views.CustomerFavouriteViewSet.as_view({'get': 'list'})),
path('customer/bookmarks/<int:reference_type>/',
views.CustomerBookmarkViewSet.as_view({'get': 'list'}))
]
You need to append router.urls to the list of existing views as
urlpatterns += router.urls. For more information, kindly look into - https://www.django-rest-framework.org/api-guide/routers/

Django Rest required parameters in URL

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)

Categories