permission to class some fields of ViewSet method Django rest framework - django

I want AllowAny permission only for the retrieve function. In my ViewSets.
class PostLanguageViewSet(viewsets.ViewSet):
permission_classes = (permissions.AllowAny,)
permission_classes_per_method = {
"retrieve": permission_classes
}
def retrieve(self, request, post_id=None, post_language_id=None, *args, **kwargs):
...
def destroy(self, request, post_id=None, post_language_id=None, *args, **kwargs):
...
def update(self, request, post_id=None, post_language_id=None, *args, **kwargs):
...
this method allows all function permission AllowAny.

If you want to be specific to action and not method. You can check the action and apply the permission accordingly.
class PostLanguageViewSet(viewsets.ViewSet):
def get_permissions(self):
if self.action == 'retrieve':
return [permissions.AllowAny()]
return super().get_permissions()

try this
class PostLanguageViewSet(viewsets.ViewSet):
def get_permissions(self):
if self.request.method == 'GET':
return [permissions.AllowAny()]
else:
return super().get_permissions()

You can use action decorator to achieve this this will override permissions and much more.
class PostLanguageViewSet(viewsets.ViewSet):
permission_classes = [permissions.AnyOtherPermissionForOtherMethods]
#action(
methods=("GET",),
url_path="get-language",
url_name="get-language",
detail=True,
permission_classes=[
permissions.AllowAny],
)
def retrive_language(self, request, post_id=None, post_language_id=None, *args, **kwargs):
#Your Code
def destroy(self, request, post_id=None, post_language_id=None, *args, **kwargs):
...
def update(self, request, post_id=None, post_language_id=None, *args, **kwargs):
...
````

Related

How to refactor ViewSet that retrieves records created by authenticated user

I need these methods to retrieve/update/delete records created by authenticated user only. I'm clearly violating one of the key principles of web/software development. How can i refactor this viewset?
class AddressViewSet(viewsets.ModelViewSet):
queryset = Address.objects.all()
serializer_class = AddressSerializer
authentication_classes = (JWTAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def list(self, request, *args, **kwargs):
self.queryset = Address.objects.filter(user=self.request.user)
return super(AddressViewSet, self).list(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
self.queryset = Address.objects.filter(user=self.request.user)
return super(AddressViewSet, self).create(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
self.queryset = Address.objects.filter(user=self.request.user)
return super(AddressViewSet, self).retrieve(request, *args, **kwargs)
def update(self, request, *args, **kwargs):
self.queryset = Address.objects.filter(user=self.request.user)
return super(AddressViewSet, self).update(request, *args, **kwargs)
def destroy(self, request, *args, **kwargs):
self.queryset = Address.objects.filter(user=self.request.user)
return super(AddressViewSet, self).destroy(request, *args, **kwargs)
try to add get_queryset method and remove filtering by user in other methods:
def get_queryset(self):
queryset = Address.objects.filter(user=self.request.user)
return queryset

How can i use middleware decorator for class-based view in djabgo?

How can i use middleware decorator for classbased view?
class APIViewMixin():
apiMiddleware = decorator_from_middleware(APISecretMiddleware)
#apiMiddleware
def dispatch(*args, **kwargs):
return super().dispatch(*args, **kwargs)
class ThemesView(APIViewMixin, View):
def get(self, request, id= None, *args, **kwargs):
if (id!= None):
serializer = vocabulary.customserializers.ThemesSerialiser(showWords=True);
return HttpResponse(serializer.serialize([vocabulary.models.Theme.objects.get(pk= id)]), content_type='application/json')
else:
serializers = vocabulary.customserializers.ThemesSerialiser(showWords=False);
return HttpResponse(serializers.serialize(vocabulary.models.Theme.objects.all()), content_type='application/json',)
this doesnt work

Django Views: Is this the correct way to use the dispatch method of a Django class based view?

I have a class based view, and from the get and post request I have been calling a method, to obtain information from information in the HttpResponseRedirect kwargs.
code:
class View1(View):
def get(self, request, *args, **kwargs):
... stuff ...
return render(request, self.template_name, self.context)
def post(self, request, *args, **kwargs):
... stuff ...
return HttpResponseRedirect(
reverse('results:report_experiment',
kwargs={
'sample_id': current_sample_id
}
))
class View2(CustomView):
def obtainInformation(self, kwargs):
sample_id = kwargs.get('sample_id', None)
self.sample_obj = Sample.objects.get(id=sample_id)
def dispatch(self, request, *args, **kwargs):
self.obtainInformation(kwargs)
return super(View2, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
... stuff ...
return render(request, self.template_name, self.context)
def post(self, request, *args, **kwargs):
... stuff ...
return HttpResponseRedirect(
reverse('results:report_experiment',
kwargs={
'sample_id': current_sample_id
}
))
My question is, when I call self.obtainInformation(kwargs) in the dispatch method, I can access any class variables I define in BOTH the get and post methods. Previously I was calling self.obtainInformation(kwargs) in both the get and post methods of view2 (so calling the method twice).
Is this a sensible way to use the dispatch method?
Yes, overriding dispatch() as you have done looks ok, and prevents the duplication of having to call obtainInformation in get() and post().
In Django 2.2+ there is a new setup() method that you could use.
class View2(CustomView):
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.obtainInformation(kwargs)

Where to check for 403 in a Django CBV?

I am making a basic app to teach beginners. Each user can write notes, but I want to make it so that a user cannot view or update a different user's notes.
I have the following view, but I had to repeat myself.
from django.core.exceptions import PermissionDenied
...
class NoteUpdate(LoginRequiredMixin, UpdateView):
...
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
raise PermissionDenied
return super(NoteUpdate, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
raise PermissionDenied
return super(NoteUpdate, self).post(request, *args, **kwargs)
I feel like there is probably a way to do this without repeating myself. Yeah, I could write a method like this and call it from both:
def check_permission(self):
if self.object.owner != self.request.user:
raise PermissionDenied
But what I really mean is am I overriding the wrong methods? Is there a more traditional way to do this? It feels a little weird overriding .get() and .post()
To answer your question: Overriding .get() and .post() is fine, since for security and integrity reasons, you would want both your get() and post() views to validate before displaying and especially modifying data. Now, if you want to refactor doing this in get or post, there are 2 easy ways of doing this:
Primary (Model Method):
models.py
class Model(models.Model):
owner = models.ForeignKey(User)
...
def deny_if_not_owner(self, user):
if self.owner != user:
raise PermissionDenied
return self.owner
views.py
class NoteUpdate(LoginRequiredMixin, UpdateView):
...
def get(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.deny_if_not_owner(request.user)
return super(NoteUpdate, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.deny_if_not_owner(request.user)
return super(NoteUpdate, self).post(request, *args, **kwargs)
///////
Alternative (Mixin):
Creating a Mixin would allow you to easily add this code to many classes if you see yourself using this validation again in the future.
class DenyWrongUserMixin(object):
def get(self):
if self.object.owner != self.request.user:
raise PermissionDenied
return super(DenyWrongUserMixin, self).get(*args, **kwargs)
def post(self):
if self.object.owner != self.request.user:
raise PermissionDenied
return super(DenyWrongUserMixin, self).post(*args, **kwargs)
and then:
class NoteUpdate(LoginRequiredMixin, DenyWrongUserMixin, UpdateView):
...
def get(self, request, *args, **kwargs):
...
def post(self, request, *args, **kwargs):
...
You can override the get method or the get_queryset method. The get_queryset will raise a 404 if the logged in user is not the owner.
def get_queryset(self):
qs = super(NoteUpdate, self).get_queryset()
return qs.filter(owner=self.request.user)
or you can just override the get method since it will be called first and then raise PermissionDenied, so no reason to override the post method as well.
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
raise PermissionDenied
return super(NoteUpdate, self).get(request, *args, **kwargs)
Then you can create a mixin and extend your views from the mixin to avoid the duplication.

Django - Mixin with TemplateView

I am trying to do something like the following to work, but I keep receiving the error 'RegionsView' object has no attribute 'method'. What am I doing wrong? Thanks
#views.py
class _LanguageMixin(object):
def dispatch(self, request, *args, **kwargs):
self.langcode = kwargs.pop("langcode")
self.language = get_object_or_404(Language, pk=self.langcode)
return super(_LanguageMixin, self).dispatch(self, request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(_LanguageMixin, self).get_context_data(self, **kwargs)
context.update({"language": self.language,
"languages": Language.objects.values_list('code',
flat=True)})
return context
class RegionsView(_LanguageMixin, TemplateView):
template_name = "regions.html"
def get_context_data(self, **kwargs):
context = super(RegionsView, self).get_context_data(self, **kwargs)
regions = #......
context.update({"regions": regions})
return context
#urls.py
url(r'^(?P<langcode>[a-zA-Z-]+)/regions/$', RegionsView.as_view(), name='regions')
return super(_LanguageMixin, self).dispatch(request, *args, **kwargs)
instead of
return super(_LanguageMixin, self).dispatch(self, request, *args, **kwargs)
(request.method is used in the dispatch function, but you use self object)