Current user in pre_delete signal in django - django

Is it possible to get a signed in django user (that calls model's delete method) in a callback connected to pre_delete signal?

The pre_delete signal doesn't pass the request instance, but you can add a decorator, that adds it, and to use that decorator on a view, which deletes the specified Model.
Assuming that this is the callback function:
def pre_delete_cb(sender, instance, using, **kwargs):
pass
which is being added in the decorator:
from django.db.models.signals import pre_delete
from functools import wraps
from django.utils.decorators import available_attrs
def pre_delete_dec(cb, sender):
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
cb.request = request # here we add the request to the callback function
pre_delete.connect(cb, sender=sender) # and connecting the real pre_delete to the callback
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
And use the decorator on the view in the way you call it - instead of pre_delete.connect(pre_delete_cb, MyModel), use:
#pre_delete_dec(pre_delete_cb, MyModel)
def myview(request):
and then in the callback you'll have access to the request as:
def pre_delete_cb(sender, instance, using, **kwargs):
current_user = pre_delete_cb.request.user
You can add this on global level, not just per view - using a Middleware:
from django.db.models.signals import pre_delete
def pre_delete_cb(sender, instance, using, **kwargs):
current_user = pre_delete_cb.request.user
class PreDeleteMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
pre_delete_cb.request = request
from myapp.models import MyModel
pre_delete.connect(pre_delete_cb, sender=MyModel)

Related

How to cache a view only for unauthenticated users?

I have a view which I want to cache only for unauthenticated users.
The view should be something like this:
#cache_page_for_guests(60 * 15)
def my_view(request):
I've looked at the docs but could not but could not find any hints about this.
Actually my question is exactly as this, which is unanswered, and I could not make sense of the comments.
So appreciate your help.
Write a custom decorator like this:
from django.views.decorators.cache import cache_page
def cache_page_for_guests(cache_life_span):
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
return cache_page(cache_life_span, key_prefix="_auth_%s_" % not request.user.is_authenticated())(view_func)(request, *args, **kwargs)
return _wrapped_view
return decorator
Then in view
#cache_page_for_guests(60 * 15)
def my_view(request):
from functools import wraps
from django.views.decorators.cache import cache_page
def cache_page_for_guests(*cache_args, **cache_kwargs):
def inner_decorator(func):
#wraps(func)
def inner_function(request, *args, **kwargs):
if not request.user.is_authenticated:
return cache_page(*cache_args, **cache_kwargs)(func)(request, *args, **kwargs)
return func(request, *args, **kwargs)
return inner_function
return inner_decorator
you can use cache_page_for_guest just like cache_page. It'll accept the same arguments as of cache_page. Based on the user's authentication, it'll show either a normal view or a cached view.
#cache_page_for_guests(60 * 15)
def my_view(request):

How to redirect a ListView to DetailView if queryset is a single object?

I have a ListView with a custom get_queryset() method and a DetailView, but I would like to avoid showing the ListView when the queryset returns only one object (I want to redirect the user to the DetailView of that object).
Is there a way to accomplish that using class based views? I tried with a redirect inside the get_queryset() of ListView, but it doesn't work and I can't find anything useful in the ListView class documentation.
You must call redirect inside get:
from django.shortcuts import redirect
from django.views.generic import ListView
class MyListView(ListView):
def get(self, request, *args, **kwargs):
queryset = self.get_queryset()
if queryset.count() == 1:
return redirect('your_detail_view_url', pk=queryset.first().pk)
return super().get(request, *args, **kwargs)
I've modified Ivan's answer a bit (the use of count() method was wrong and I also need to use slug instead of pk for my DetailView), so the solution was:
from django.shortcuts import redirect
from django.views.generic import ListView
class MyListView(ListView):
def get(self, request, *args, **kwargs):
queryset = self.get_queryset()
if len(queryset) == 1:
return redirect('your_detail_view_url', queryset.first().pk)
return super().get(request, *args, **kwargs)

How can I call a function within a DRF view?

I am using Djano REST Framework for constructing APIs .I want something like below
def addTwoNumber(a,b):
return a+b
class MyView(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
my_result=addTwoNumber(request.data.get('firstnum'),request.data.get('secondnum'))
return Response(data={"my_return_data":my_result})
That is , I want a view that doesn't deals with the queryset &serializer_class attributes. Is it possible ? Can anyone help me ?
Why are you using ModelViewSet? Just use APIView http://www.django-rest-framework.org/api-guide/views/
# views.py
from rest_framework.views import APIView
def addTwoNumber(a,b):
return a+b
class MyView(APIView):
def post(self, request, *args, **kwargs):
my_result=addTwoNumber(request.data.get('firstnum'),request.data.get('secondnum'))
return Response(data={"my_return_data":my_result})
# urls.py
urlpatterns = [
url(r'^myview/$', MyView.as_view()),
...
]

Decorator for CBV

I have this CBV:
class GetStuff(View):
def get(self, request, company_id):
...
I want to decorate the get function with a custom function which takes the request and company_id arguments and check some permissions.
Any idea on how to achieve this? Most information I've found about decorators focus on FBV.
This is what I have so far:
def custom_decorator(func_view):
def wrapper(request, company_id):
if not request.user.is_staff:
# do_something()
return func_view(request, company_id)
return wrapper
You have to decorate the dispatch method
To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the dispatch() method of the class. django doc
from django.utils.decorators import method_decorator
class GetStuff(View):
#method_decorator(custom_decorator)
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
or
#method_decorator(custom_decorator, name='dispatch')
class GetStuff(View):
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
you can find other decorating techniques in the docs

Problems importing mixin into another app for apply to Class Based View

I have the file userprofiles/mixins.py in which I've created this mixin
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class LoginRequiredMixin(object):
#method_decorator(login_required(login_url = '/login/'))
def dispatch(self, request, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
In my file userprofiles/views.py I have the following class based view named ProfileView of this way:
from .mixins import LoginRequiredMixin
class ProfileView(LoginRequiredMixin,TemplateView):
template_name = 'profile.html'
def get_context_data(self, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
context.update({'userprofile': self.get_userprofile()})
return context
def get_userprofile(self):
return self.request.user.userprofile
In this class based view named ProfileView I could inherit from LoginRequiredMixin without any trouble
These mixin LoginRequiredMixin I also applied to the class based view named AlbumListView which is located in other module or app artists/views.py.
The class based view AlbumListView is this:
from sfotipy.userprofiles.mixins import LoginRequiredMixin
class AlbumListView(LoginRequiredMixin,ListView):
model = Album
template_name = 'album_list.html'
def get_queryset(self):
if self.kwargs.get('artist'):
queryset = self.model.objects.filter(artist__slug__contains=self.kwargs['artist'])
else:
queryset = super(AlbumListView, self).get_queryset()
return queryset
The unique way for my IDE don't mark error when I import the LoginRequiredMixin for I inherit from it is of this way:
from sfotipy.userprofiles.mixins import LoginRequiredMixin
I know that this import way is not correct, because is a absolute import and the right way is doing a relative import.
Other way in where should work is this:
from userprofiles.mixins import LoginRequiredMixin
But, when I want test I get the following error message...
How to can I do this import and that works?
Thanks for the orientation :D