Django Custom View Decorators - python

Alright I have a method called no_m in the user class and i've not written a decorator before, but basically I need to redirect the user to another URL if they pass this. I have created a file called decorators.py in a dir called accounts and i'm guessing the decorator is imported correctly, however I cannot get it to work. Heres what I have:
def no_m(view_func):
def _wrapped_view_func(request, *args, **kwargs):
try:
if request.user.is_m():
# quick test
return HttpResponseRedirect('http://google.com')
else:
return view_func(request, *args, **kwargs)
except:
return _wrapped_view_func
All it needs to do is redirect users if they pass that test, I don't know what the URL needs to be yet so it's just google for now. Any ideas? Like I said, i've not written decorators before so it's all new to me. Thankyou.
Another thought: would it be possible to render a template page?

You're missing a step in the decorator, or rather you have a step confused. It's the outer function that must return the inner function (_wrapped_view_func), and it must always do so: that's what takes the place of the original function when it is called.
I'm not sure what the except clause is there for. Apart from it always being a bad idea to use a blank except - that catches everything, including things like ctrl-c - exceptions in Django functions are usually handled by the middleware, rather than the decorator. I would just remove it.
So the code should be:
def no_m(view_func):
def _wrapped_view_func(request, *args, **kwargs):
if request.user.is_m():
# quick test
return HttpResponseRedirect('http://google.com')
else:
return view_func(request, *args, **kwargs)
return _wrapped_view_func

Related

Use a decorator to add in a variable to request

I know the suggested way to do this is middleware, but I'd like to implement the following to add in an api_env variable, accessed at run-time, so that my view can access it:
#api_wrapper
def my_view(request):
print api_env # this is the variable I want, such as request.epi_env
And the decorator:
def api_wrapper(func):
def api_inner(request):
request.api_env = 'something'
return func(request)
return api_inner
What's the best way to do this? I have about 100 functions to wrap so I don't want to add in a new parameter for every function but would just like to use the simplest approach to pass that 'env' variable. How should I do it.
You can generalize this to work with an arbitrary number of positional and named parameters. Furthermore you might want to use update_wrapper [Python-doc], to add attributes like csrf_exempt to the "output" view, otherwise the #csrf_except will not be available in the outer function:
from functools import update_wrapper
def api_wrapper(func):
def api_inner(request, *args, **kwargs):
request.api_env = 'something'
return func(request, *args, **kwargs)
update_wrapper(api_inner, func, assigned=())
return api_inner
That being said, this to some extent shows that using class-based views might be better in this case, since then one can define a mixin, and just mix it in the method resolution order (MRO) as a reusable component. In that case, one often do not have to take into account more complicated logic like the parameters, or the attributes that are added to the function, since a class-based view takes care of this itself.
Try this:
def api_wrapper():
def decorator(view_func):
#wraps(view_func)
def _wrapped_view(req, *args, **kwargs):
req.env = 'something'
return view_func(req, *args, **kwargs)
return _wrapped_view
return decorator

Class Based View and decorators

I have a FormView for displaying form, and code goes on like this:
class AddProject(FormView):
template_name = "project/add_project.html"
#method_decorator(check_user_type)
def dispatch(self,request, *args, **kwargs):
return super(AddProject,self).dispatch(request, *args, **kwargs)
def get_form_class(self):
return AddProjectForm
def form_valid(self,form):
#do validation, return response
the decorator check_user_type is like this:
def check_user_type(func):
def wrapped_func(request, *args, **kwargs):
kwargs['invalid_user'] = True
return func(request,*args, **kwargs)
return wrapped_func
In my decorator I want To make sure that only certain type of user get to see the form, i.e if request.user.Iam == 'Architect' or request.user.Iam == 'Interior Designer' only see the form and others see a message "Only architects/Interior Designer get to upload photos".For this i want to insert a variable 'invalid_user' to be passed along, depending on which i display the form or the message.
Problem is I am unable to pass the variable :( alongwith it .. and a doubt.. if i have correctly devised the idea to do so.. ?
If I understand you correctly, you want to pass this argument to check_user_type decorator, right? Then you need to nest another function in your decorator, using closure to set variables inside it. Something like
def check_user_type(parameter):
def check_user_type_inner(func):
def wrapped_func(request, *args, **kwargs):
... # parameter here depends on argument passed to most outer function
return func(request,*args, **kwargs)
return wrapped_func
return check_user_type_inner
then parameter is available inside scopes of both inner functions.

Django ListView mixin from Django documentation throws an error, why

I'm trying to adopt the Django documentation example on using class based views with mixins in order to be able to make a simple way of downloading the objects in a list view in CSV format, but I am failing miserably and do not really see what I am doing wrong.
I have a view defined as:
class MyObjectList(CSVResponseMixin,
MultipleObjectTemplateResponseMixin,
BaseListView
):
object_type = None
def get_context_data(self, **kwargs):
object_type = self.object_type
...some code...
return context
def render_to_response(self, context, **kwargs):
if self.request.GET.get('format', '') == 'csv':
return CSVReponseMixin.render_to_response(self, context, **kwargs)
else:
return MultipleObjectTemplateResponseMixin.render_to_response(self, context, **kwargs)
the mixin is:
class CSVResponseMixin(object):
def render_to_response(self, ctx, **kwargs):
return self.HttpResponse.render_to_response(self.convert_to_csv(ctx), **kwargs)
def conver_to_csv(ctx):
return do_csv_magic(ctx)
and in urls.py the view is called as:
url(r'^list/foos/$',
MyObjectList.as_view(object_type="someobject", model=models.MyModel),
name="myobjects",
)
However when I try to access the view without the ?format=csv query, I get a TypeError
Exception Value: __init__() got an unexpected keyword argument 'request'
Exception Location: /usr/lib/python2.6/site-packages/django/views/generic/base.py in render_to_response, line 97
EDIT: I added some details to the question and ended up implementing this with a different approach, but I still want to know what I was doing wrong.
In short, you're overdoing it. I'm not sure what is your intention here, but I've learned that the best approach is to find the closest generic view to what you're trying to do and simply extend it in views.py. Examples are many, but I invite you to check my code at https://bitbucket.org/BerislavLopac/resume/src/d7cfcf9c370b/resume_app/myproject/web/views.py.
According to the docs, render_to_response only takes the following arguments: template_name, dictionary, context_instance, mimetype
Therefore within FooResponseMixin when you're calling:
self.HttpResponse.render_to_response(self.mutilate_context(ctx), **kwargs)
You're passing in extra arguments within kwargs that render_to_response doesn't accept. Either remove the **kwargs or assign only what you need from it to variables to pass in to the accepted arguments.

Unit testing decorators in Python

I wrote the following decorator to be used in some Django views where I don't want the user to be logged in (like register and forgot-password):
def not_logged_in(view, redirect_url=None):
def _wrapper(request, *args, **kwargs):
if request.user.is_authenticated():
return HttpResponseRedirect(
redirect_url or '/'
)
return view(*args, **kwargs)
return _wrapper
Once I have it, I can easily write:
#not_logged_in
def register(request):
...
I have written unit tests for the views that are using it, and it is working without problems, but I'm wondering what would be the best way of unit testing the not_logged_in function alone?
I suppose that you can easily mock the request object, then decorate a trivial function with your decorator and pass that request a parameter.
I also suppose that your _wrapper does not really have an unused request parameter?

In a Django manager, why use self.get_query_set().get(kwarg=val) rather than self.get(kwarg=val)?

In a prior question I asked, where a Manager's method looked like:
def activate(key):
try:
profile = self.get(key=key)
except self.model.DoesNotExist:
return None
if not profile.key_expired():
# -> Activate user
return user
return None
It was suggested to use self.get_query_set().get(key=key) instead of self.get(key=key) within the manager method. I was wondering what the reason for this is, as the former seems much more verbose?
I guess the author just likes being verbose. There is no difference. The Manager class' get method is defined as:
def get(self, *args, **kwargs):
return self.get_query_set().get(*args, **kwargs)
You can see it for yourself in django/db/models/manager.py

Categories

Resources