Django context processor to check user permissions - django

I would like to write a context processor which will look the existing context, and if there is a form present, check that the user has permission to edit that object. If not, I will overwrite that context variable.
How do I read the existing context from inside a context processor?
Edit:
The reason for using this type of context processor is to avoid having the check permissions with if statements in each view. Some users will have read/write access, others will have read-only and others will have no access, at the object level. I would like to manipulate the form accordingly after the view has been processed.

Why not just wrap the view with a user_passes_test decorator? You can run any checks you like there -- no hacks required.

You should use django-authority, or any authority package. Although, I cannot guarantee that they don't rely on hacks themselves, but at least you wouldn't be alone using the hacks. That said, here is a working solution to your issue:
Monkey patch django.template.base.RequestContext, to set request.context = self.
Create a template context processor, which can use request.context
Add the context processor to settings, and you're good to go
Here is an example, which you can just paste in settings.py, it works:
def print_context(request):
print request.context.get('form', 'No form in this context')
return {}
from django.template.base import RequestContext
orig_init = RequestContext.__init__
def new_init(self, request, **kwargs):
request.context = self
orig_init(self, request, **kwargs)
RequestContext.__init__ = new_init
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.request',
'settings.print_context',
)
Note that you are working against Django's design, I posted this answer assuming you know what you're doing - you know both Python and Django very well.

Related

User is not being passed to template TEMPLATE_CONTEXT_PROCESSORS

Following the docs
If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every
RequestContext will contain these variables:
user – An auth.User instance representing the currently logged-in user
(or an AnonymousUser instance, if the client isn’t logged in). perms –
An instance of django.contrib.auth.context_processors.PermWrapper,
representing the permissions that the currently logged-in user has.
I added these lines to settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
)
Rendering my templates as follows
def profile_view(request):
return render_to_response('profile.html', { 'profile' : 1 })
I was under the impression that the request context along with the user will be passed automatically. Is that not the case, or should i explicitly add context_instance=template.RequestContext(request)) to every view ?
Thanks.
Yes, if you want to use render_to_response and you want template context data available, you should pass in a RequestContext instance.
Alternatively, if you render your page with django.shortcuts.render, the RequestContext will be injected automatically.
You may find this answer useful, too: Django - what is the difference between render(), render_to_response() and direct_to_template()?

Django View Preprocessing

Just looking for some guidance here on how to implement this concept in "Django fashion".
I am using dbsettings to define site level variables. On each request I want to check one of my site level variables and render a different response based on the value of that variable.
Now I realize I could easily accomplish this just by adding a simple if statement in each of my view functions, but I thought there may be a way I could apply this at a higher level?
Let me know if there is a way to "globalize" this functionality across all requests. Thanks.
Actually, what you really probably want is middleware, if the setting is going to affect the template that's chosen in the first place, or substantially affect the response. It is a good deal more flexible than using a template context processor, which is more appropriate if you simply want to add a couple variables to your context.
You create a middleware.py in your app, which might contain something like:
from django.conf import settings
class MyMiddleware(object):
def process_request(self, request):
request.my_app_setting = settings.MY_APP_SETTING
Don't forget to add your class to your MIDDLEWARE_CLASSES setting.
You can use custom template context processors for passing "global" context to your views.
To accomplish this, create a new contextprocessors.py somewhere in your application with code similar to the example below (all it has to do is return a dict). Then add the path to the file with the function in the TEMPLATE_CONTEXT_PROCESSORS tuple in your settings.py (ie: yourapp.contextprocessors.resource_urls).
from django.conf import settings
def resource_urls(request):
"""Passes global values to templates."""
return dict(
TIME_ZONE = settings.TIME_ZONE,
)
Now you can refer to these keys in your templates as expected: {{ TIME_ZONE }}.

how to pass variables to templates only for some applications

I have a django project, with a lot of applications. Now i need to add some variables to the context dictionary only for some applications.Does django provide any option to achieve this?
I know decorator can be helpful.Is there any other way,like a middleware / context processor that runs automatically but only for specific applications.
A context processor like this can do what you need:
from django.core.urlresolvers import resolve
def app_var(request):
if resolve(request.path).app_name == 'YOUR_APP_NAME':
return {'CUSTOM_VAR': 'VALUE'}
return {}
to install the context processor, put the code in any file you like, and add a entry referencing (e.g. 'folder.file.app_var') it in the CONTEXT_PROCESSORS in your settings.py.

How do I pass context to a template without actually specifying it in all views?

I have a few views and they all work good and all use templates that extend one base template that outputs the core HTML, the header, footer, navigation and so on. Happy family.
now, I want to play with sessions on the pages and since you can't get access to user's session information from the template without actually passing it from the view (correct me where I'm wrong) I have two options:
add session data to the rest of the bits I pass as context to HTML templates in views (not sure if this is a good way to go)
somehow inherit all existing views from a view that will be always pushing context to templates being processed - this way I don't have to worry about anything else I may want to add to my pages in the future - is this possible?
I'm very new to django and there may be other proper way of doing this - all your suggestions are much appreciated.
I think adding in a context processor is a very easy way to do this.
You could either write your own or use this one:
DJANGO.CORE.CONTEXT_PROCESSORS.REQUEST
http://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request
Then you will have the request in your template, and could get to the session with request.session
If you do that though, make sure that you pass the RequestContext along in your views to the templates, something like this:
from django.template import RequestContext
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
Also modify your settings.py to add in the context processor
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + (
"django.core.context_processors.request",
)

Non-destructively handling Django user messages

I'm displaying user messages through templates using RequestContext in Django, which gives access to user messages through {{messages}} template variable - that's convenient.
I'd like user him/herself delete the messages - is there a way to do it in Django without rewriting much code? Unfortunately Django automatically deletes messages at each request - not very useful in this case.
Django doc says:
"Note that RequestContext calls get_and_delete_messages() behind the scenes"
Would be perfect if there were a way to simply turn off the automatic deletion of messages!
NOTE: Unfortunately solution below makes admin interface unusable. I don't know how to get around this, really annoying.
EDIT - found a solution - use custom auth context processor that calls user.message_set.all() as Alex Martelli suggested. There's no need to change the application code at all with this solution. (context processor is a component in django that injects variables into templates.)
create file myapp/context_processors.py
and replace in settings.py in TEMPLATE_CONTEXT_PROCESSORS tuple
django.core.context_processors.auth with myapp.context_processors.auth_processor
put into myapp/context_processors.py:
def auth_processor(request):
"""
this function is mostly copy-pasted from django.core.context_processors.auth
it does everything the same way except keeps the messages
"""
messages = None
if hasattr(request, 'user'):
user = request.user
if user.is_authenticated():
messages = user.message_set.all()
else:
from django.contrib.auth.models import AnonymousUser
user = AnonymousUser()
from django.core.context_processors import PermWrapper
return {
'user': user,
'messages': messages,
'perms': PermWrapper(user),
}
I know it sounds like a strange approach, but you could copy into your own list
request.user.message_set.all()
before instantiating RequestContext, and later put them back in..