Use #staticmethod in Django Rest Framework endpoint method? - python

Should I be defining post, get, etc methods of a Django Rest Framework APIView as static?
class HomeView(APIView):
def get(request):
etc...
or
class HomeView(APIView):
#staticmethod
def get(request):
etc...
What are the pros/cons of each way?
Thank you

DRF does not declare get and post as static methods and neither should you. Here's how the defaults are configured in DRF generics.
It is common in DRF to reference instance methods such as self.get_object and self.get_serializer from within get and post.
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for deleting a model instance.
"""
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for updating a model instance.
"""
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)

As DRF document, You seems do not need to add #staticmethod decorator

DRF won't function correctly without passing ' self '.
PyCharm does mark these methods as 'maybe static', this is false!
Making them static breaks the calling of the method, since the caller method provides the 'self', and your method does not except it. Essentially your still get the self aliased as request, but your other args are ignored. It is possible that this happens silently, that makes it a fairly nasty bug to find :(.
In terms of performance a 'static' or regular function based view should be slightly faster, since you avoid the creation of the instance, but the difference is probably so small that it is hardly noticeable.

Related

Move some code from GenericAPIView post method to other methods in django

I have my django GenericAPIView with post method, I want some part of the logic which uses self to be in some other method and exccute that code when my post method gets hit
class MyAPIView(GenericAPIView):
def post(self, request, *args, **kwargs):
You can do something like this!
class MyAPIView(CreateAPIView):
def custom_function(self):
"""place your logic here!"""
pass
def post(self, request, *args, **kwargs):
# This will call our custom function as soon as post request is hit
self.custom_function()
return super().post(request, *kwargs, *args)

Invoke function after all methods Django Rest Framework ModelViewSet

I would like to invoke a function after all methods in a ModelViewSet. The function itself will be used as an external event log. So the response from the views would not be modified. I was not able to figureout how to achieve this. My ModelViewSet is a very basic one with serializer class and queryset defined.
Any help would be appreciated.
Override the dispatch method of view.
class MyDRFView(...):
def my_custom_logging_method(self, request, response, *args, **kwargs):
# do something useful here.....
...
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
self.my_custom_logging_method(request, response, *args, **kwargs)
return respons
You can always override any python class. Overriding all the method can be little trickier and I guess will just create unnecessary logs. You should only override the method that is really of importance. Here is an example:
class LoggingModelViewSet(viewsets.ModelViewSet):
def perform_create(self, serializer):
print('Invoked perform_create')
serializer.save(owner=self.request.user)
def finalize_response(self, request, response, *args, **kwargs):
xresponse = super().finalize_response(request, response, *args, **kwargs)
print('finalize_response', xresponse)
return xresponse
and more like this... you should see the source here https://github.com/encode/django-rest-framework/blob/master/rest_framework/viewsets.py#L217 It is not so tricky.

How to create a decorator that works on class-based views function and class?

I have some decorators that can be used on top of class-based views like below :
#method_decorator(auth0_login_required, name='dispatch')
class MyClass(DetailView):
...
I'd like to also be able to use it on top of functions inside class-based views like so :
class MyClass2(DetailView):
...
def get(self, request, *args, **kwargs):
...
#auth0_login_required
def post(self, request, *args, **kwargs):
...
The problem is that the decorator doesn't work the same for these two use case, I'd like to avoid creating two decorators that does the same work.
Here's what the decorator looks like :
def auth0_login_required(function):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated or request.user.is_staff:
...
...
return function(request, *args, **kwargs)
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap
For the second use case I have to add self as parameter right before request.
I was wondering if there was a way to make it work both ways with one decorator?
You can decorate the post method of your class based view with name='post'.
#method_decorator(auth0_login_required, name="post")
class MyClass2(DetailView):
...
def get(self, request, *args, **kwargs):
...
def post(self, request, *args, **kwargs):
...
Or you can decorate the post method directly. Since it's a method, you should still use method_decorator.
class MyClass2(DetailView):
...
def get(self, request, *args, **kwargs):
...
#method_decorator(auth0_login_required)
def post(self, request, *args, **kwargs):
...
If you have a function based view, then you don't need to use method_decorator.
#auth0_login_required
def my_function_based_view(request, *args, **kwargs):

Django class view: __init__

I want to get <Model> value from a URL, and use it as an __init__ parameter in my class.
urls.py
url(r'^(?P<Model>\w+)/foo/$', views.foo.as_view(), name='foo_class'),
views.py
class foo(CreateView):
def __init__(self, **kwargs):
text = kwargs['Model'] # This is not working
text = kwargs.get('Model') # Neither this
Bar(text)
...
Clearly, I'm missing something, or my understanding of URL <> class view is wrong.
You should override dispatch method for such use cases.
class Foo(CreateView):
def dispatch(self, request, *args, **kwargs):
# do something extra here ...
return super(Foo, self).dispatch(request, *args, **kwargs)
For your specific scenario, however, you can directly access self.kwargs as generic views automatically assign them as an instance variable on the view instance.

Dajaxice with Class Based View

Is there a way to user dajaxice with django class based views?
I'm trying this, but not having much success:
class FavoriteEnroledTrainee(SessionMixin, View):
def get(self, request, *args, **kwargs):
print 'here'
#method_decorator(dajaxice_register(method='GET', name='company.favorite'))
def dispatch(self, *args, **kwargs):
return super(FavoriteEnroledTrainee, self).dispatch(*args, **kwargs)
I can see the dajaxice is able to fetch the view but nothing gets printed.
You cannot register the dispatch method, because it's not the view entry point. Dajaxice will try to call dispatch directly, but this won't work because it's not a fully functionnal view.
You should register the result of the *as_view* call :
class FavoriteEnroledTrainee(SessionMixin, View):
def get(self, request, *args, **kwargs):
print 'here'
favorite_enroled_trainee = dajaxice_register(method='GET', name='company.favorite')(FavoriteEnroledTrainee.as_view())

Categories