Reputation: 5726
I created a middleware which allows me to use a list of dictionaries to specify some access rules for any of my views. Each of these dictionaries looks like this:
REQUIREMENTS=(
{'viewname':'addtag',
'permissions':'can_add_tags'},
{'regex':re.compile(r'^somestart'),
'user_check':lambda request:request.user.username=='sam'}
)
In my middleware i then try to find out which of those requirements match the current request. To do so, i filter the complete REQUIREMENTS, and in the filter function i use this code to check if the path matches:
def process_request(self,request):
def path_matches(self,req):
path_matches = False
if (req.has_key('url') and req['url'] == request.path ) or\
(req.has_key('regex') and req['regex'].search(request.path)) or\
(req.has_key('viewname') and resolve(request.path).url_name==req['viewname']):
path_matches=True
return path_matches
requirements = filter(path_matches,REQUIREMENTS)
# now use the returned requirements to determine if a user
# matches the requirement and
My question now is: in which order should i use the checks? It's pretty clear that the check for the url is the fastest, so this has to be first. But then the question is if the regex search or django's url resolve function should follow first.
As i do not have any performance issues right now, this is more of an academic question. And if someone would have an all better solution to solve this, that would be even better.
edit:
To react to the given answers: what i'm trying to do is to create a possibility to restrict views of several external apps in a single file. So decorators are not an option, as long as i do not want to do something like this:
from ext_app1 import view1,view2
from ext_app2 import view3
@permission_required('can_do_stuff')
def view1_ext(*args,**kwargs):
return view1(args,kwargs)
which would lead to rewriting the url specifications each time i change permissions. I want to avoid that. Additionally my solution allows for a user_check function to do a check on a user like this:
def check_user(user):
if len(Item.objects.get(creator=user,datetime=today)) > 3:
return False
return True
That would be a simple way to, ie., restrict how many items a user can upload each day. (Ok, that would be possible with user_passes_test as well).
One more thing is that i sometimes want to check for a permission only if the request is a POST, or if the request contains a certain key:value pair (like 'action':'delete'
should require the permission, while 'action':'change'
should be allowed for anyone). This could be done with a custom decorator as well, but as soon as i would need a new check, i would need a new decorator.
Upvotes: 0
Views: 120
Reputation: 1989
You might be looking for user_passes_test decorator.
You can decorate the views you need instead of using a middleware.
The code will looks like this:
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda user: user.has_perm('model.can_add_tags') \
and user.username == 'sam')
def my_view(request):
...
Upvotes: 1
Reputation: 30183
If you're using Django's built in user authentication and permissions system (django.contrib.auth
) then you should consider using the views decorators it provides instead of a middleware. These give you several advantages:
For simple situations you can use the login_required
and permission_required
decorators, and for a more complex condition the user_passes_test
decorator allows you to check if the user passes any condition you care to specify.
The code looks something like this for a view function (example is lifted from the documentation):
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote')
def my_view(request):
...
If you're using class-based views then it looks a little different (again, this example is lifted from the documentation):
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
If you have a good reason not to use Django's permissions system, then you can still adopt a similar approach. The code for the django.contrib.auth
decorators could easily be used as the basis of your own decorators.
Upvotes: 1