render from another method - django

I have two urls defined in my urls.py one to go to homepage.html and one to go to unauthorized.html. I created the following in my views.py. My goal was to have a common method called "check_authorized" and to call that from my homepage method and if it met a condition here if 'isFoo' is false to call 'unauthorized' method so that it can redirect to the unauthorized.html. However when I call homepage.html it doesn't redirect to that page instead it stays on homepage.html ( I even removed the if block so that the code path hist the 'unauthorized' method directly). Here is the view.py
def check_authorized(request):
ctx = RequestContext(request)
posixGroups = []
#Have some logic to add groups
ctx['isFoo'] = 'foo' in posixGroups
if not ctx['isFoo']:
unauthorized(request)
return ctx
def unauthorized(request):
ctx = RequestContext(request)
return render_to_response('unauthorized.html', ctx)
def homepage(request):
ctx = check_authorized(request)
return render_to_response('homepage.html', ctx)
In my Urls.py I have in the url patterns
(r'^unauthorized', views.unauthorized),
(r'', views.homepage),
Update:
Adding decorator class
def check_authorized(request, login_url=None):
def is_authorized(u):
user = request.META['REMOTE_USER']
posixGroups = []
#Code to get posix groups
isFoo = 'foo' in posixGroups
if not isFoo:
return False
return True
return user_passes_test(is_authorized, login_url=login_url)
def unauthorized(request):
ctx = RequestContext(request)
return render_to_response('unauthorized.html', ctx)
#check_authorized(login_url='unauthorized')
def homepage(request):
ctx = check_authorized(request)
return render_to_response('homepage.html', ctx)
Error
Exception Type: TypeError
Exception Value:
check_authorized() takes at least 1 argument (1 given)

This is a very good use case for a decorator, like login_required:
from django.contrib.auth.decorators import login_required
#login_required(login_url='/accounts/login/')
def my_view(request):
...
It would redirect the user to the login_url if it is not authenticated.
So, since you have a custom logic that decides whether to redirect the user to login_url or not, you need a custom decorator that would be similar to Django built-in decorators:
from django.contrib.auth.decorators import user_passes_test
def check_authorized(request, login_url=None):
def is_authorized(user):
# Have some logic that returns True/False
...
return user_passes_test(is_authorized, login_url=login_url)
#check_authorized(login_url='unauthorized/')
def homepage(request):
return render_to_response('homepage.html')
Also see:
group_required decorator

Related

Get current user in djangocms plugins (CMSPluginBase)

I want to create a plugin whose content is different depending on the user who opens the page.
I tried :
#plugin_pool.register_plugin # register the plugin
class RubriquePluginPublisher(CMSPluginBase):
"""
RubriquePluginPublisher : CMSPluginBase
"""
model = RubriquePluginModel # model where plugin data are saved
module = "Rubrique"
name = "Rubrique plugin" # name of the plugin in the interface
render_template = "commission/rubrique_plugin.html"
def render(self, context, instance, placeholder):
context.update({'instance': instance})
query_set = Article.objects.filter(rubriques=instance.rubrique).filter(contact=self.request.user)
page = context['request'].GET.get('page_{}'.format(instance.rubrique.name)) or 1
paginator, page, queryset, is_paginated = self.paginate_queryset(page, query_set, 3)
context.update({'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'articles': queryset
})
return context
But self.request doesn't exist in class CMSPluginBase.
How can I access the current user?
EDIT :
I found a workaround. I created a middleware that returns the current request. It works well but don't hesitate if someone has a better solution.
from django.utils.deprecation import MiddlewareMixin
_requests = {}
def get_current_request():
t = current_thread()
if t not in _requests:
return None
return _requests[t]
class RequestMiddleware(MiddlewareMixin):
def process_request(self, request):
_requests[current_thread()] = request
#plugin_pool.register_plugin # register the plugin
class PrivateRubriquePluginPublisher(CMSPluginBase):
def render(self, context, instance, placeholder):
req = get_current_request()
query_set = Article.objects.filter(contact=req.user)
As far as I know the recommended approach would be:
Add django.template.context_processors.request to your context_processors in settings.py - this will add a request variable to your context.
Then in CMSPluginBase.render you could access it as following:
def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request: HttpRequest = context['request']

Django, extract kwargs from url

Suppose I have a url pattern
url(r'^/my_app/class/(?P<class_id>\d+)/$', my_class.my_class, name='my_class')
For a given url
http://example.com/my_app/class/3/, I'd like to get class_id.
I can do this with regex myself.
I am wondering if there's a utility function for this since Django is already doing this to resolve url to a view.
There is an example of using resolve() function in Django docs. Value of next variable has HTTP url to be parsed with urlparse() / resolve():
https://docs.djangoproject.com/en/1.11/ref/urlresolvers/#resolve
from django.urls import resolve
from django.http import HttpResponseRedirect, Http404
from django.utils.six.moves.urllib.parse import urlparse
def myview(request):
next = request.META.get('HTTP_REFERER', None) or '/'
response = HttpResponseRedirect(next)
# modify the request and response as required, e.g. change locale
# and set corresponding locale cookie
view, args, kwargs = resolve(urlparse(next)[2])
kwargs['request'] = request
try:
view(*args, **kwargs)
except Http404:
return HttpResponseRedirect('/')
return response
If you are using generic views or just views, you can do something like this:
class myView(View): # or UpdateView, CreateView, DeleteView
template_name = 'mytemplate.html'
def get(self, request, *args, **kwargs):
context = {}
class_id = self.kwargs['class_id']
# do something with your class_id
return render(request, self.template_name, context)
# same with the post method.
Yes, you can do like this
def my_class(request, class_id):
# this class_id is the class_id in url
# do something;

#login_required for multiple views

I am going to have probably over 20 views. All of them require the user to authenticate first. Do I have to put #login_required over each one or is there a better way?
https://docs.djangoproject.com/en/1.6/topics/auth/default/#django.contrib.auth.decorators.login_required
I ended up making a new file in my npage app directory called lockdown.py and pasted the code from this solution:
import re
from django.conf import settings
from django.contrib.auth.decorators import login_required
class RequireLoginMiddleware(object):
"""
Middleware component that wraps the login_required decorator around
matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
settings.py. For example:
------
LOGIN_REQUIRED_URLS = (
r'/topsecret/(.*)$',
)
LOGIN_REQUIRED_URLS_EXCEPTIONS = (
r'/topsecret/login(.*)$',
r'/topsecret/logout(.*)$',
)
------
LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
be a valid regex.
LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
define any exceptions (like login and logout URLs).
"""
def __init__(self):
self.required = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS)
self.exceptions = tuple(re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS)
def process_view(self, request, view_func, view_args, view_kwargs):
# No need to process URLs if user already logged in
if request.user.is_authenticated():
return None
# An exception match should immediately return None
for url in self.exceptions:
if url.match(request.path):
return None
# Requests matching a restricted URL pattern are returned
# wrapped with the login_required decorator
for url in self.required:
if url.match(request.path):
return login_required(view_func)(request, *view_args, **view_kwargs)
# Explicitly return None for all non-matching requests
return None
After that in settings.py I added this to MIDDLEWARE_CLASSES...
MIDDLEWARE_CLASSES = (
# ...
'npage.lockdown.RequireLoginMiddleware',
)
And of course, these lines to lock the whole site down:
LOGIN_REQUIRED_URLS = (
r'/(.*)$',
)
LOGIN_REQUIRED_URLS_EXCEPTIONS = (
r'/login(.*)$',
r'/logout(.*)$',
)
As of Django 3+, you have to do like followings:
Step 1: Create a new file anything.py in your yourapp directory and write the following:
import re
from django.conf import settings
from django.contrib.auth.decorators import login_required
//for registering a class as middleware you at least __init__() and __call__()
//for this case we additionally need process_view() which will be automatically called by Django before rendering a view/template
class ClassName(object):
//need for one time initialization, here response is a function which will be called to get response from view/template
def __init__(self, response):
self.get_response = response
self.required = tuple(re.compile(url) for url in settings.AUTH_URLS)
self.exceptions = tuple(re.compile(url)for url in settings.NO_AUTH_URLS)
def __call__(self, request):
//any code written here will be called before requesting response
response = self.get_response(request)
//any code written here will be called after response
return response
//this is called before requesting response
def process_view(self, request, view_func, view_args, view_kwargs):
//if authenticated return no exception
if request.user.is_authenticated:
return None
//if found in allowed exceptional urls return no exception
for url in self.exceptions:
if url.match(request.path):
return None
//return login_required()
for url in self.required:
if url.match(request.path):
return login_required(view_func)(request, *view_args, **view_kwargs)
//default case, no exception
return None
Step 2: Add this anything.py to Middleware[] in project/settings.py like followings
MIDDLEWARE = [
// your previous middleware
'yourapp.anything.ClassName',
]
Step 3: Also add the following snippet into project/settings.py
AUTH_URLS = (
//i am disallowing all url
r'(.*)',
)
NO_AUTH_URLS = (
r'/admin(.*)$',
)

django anonymous_required custom decorator clashing with password_reset views

I am using the built-in auth_views.password_reset(_confirm, _done, _complete)functionalities, and I would like to limit access to these views only to non-logged-in (anonymous) users, because it doesn't make sense for logged-in users to reset their password.
I found the opposite of #login_required at this link: http://passingcuriosity.com/2009/writing-view-decorators-for-django/
The decorator works for auth_views.password_reset. I use it in my urls as such
url(r'^password/reset/$',
anonymous_required(auth_views.password_reset),
name='auth_password_reset'),
For some reason it does not work with the other 3 views. For example the following url:
url(r'^password/reset/done/$',
anonymous_required(auth_views.password_reset_done),
name='auth_password_reset_done'),
gives me the following error:
Exception Value:
Reverse for 'django.contrib.auth.views.password_reset_done' with arguments '()' and keyword arguments '{}' not found.
Can anyone tell me why?
The decorator code given is:
def anonymous_required(function=None, home_url=None, redirect_field_name=None):
"""Check that the user is NOT logged in.
This decorator ensures that the view functions it is called on can be
accessed only by anonymous users. When an authenticated user accesses
such a protected view, they are redirected to the address specified in
the field named in `next_field` or, lacking such a value, the URL in
`home_url`, or the `USER_HOME_URL` setting.
"""
if home_url is None:
home_url = settings.USER_HOME_URL
def _dec(view_func):
def _view(request, *args, **kwargs):
if request.user.is_authenticated():
url = None
if redirect_field_name and redirect_field_name in request.REQUEST:
url = request.REQUEST[redirect_field_name]
if not url:
url = home_url
if not url:
url = "/"
return HttpResponseRedirect(url)
else:
return view_func(request, *args, **kwargs)
_view.__name__ = view_func.__name__
_view.__dict__ = view_func.__dict__
_view.__doc__ = view_func.__doc__
return _view
if function is None:
return _dec
else:
return _dec(function)
I see password_reset includes a reverse of the routine mentioned in the error message. Is it trying to do this reverse but you have overridden it, or not included it in the urls.conf?
django/contrib/auth/views.py:141-142 (v1.3)
if post_reset_redirect is None:
post_reset_redirect = reverse('django.contrib.auth.views.password_reset_done')

What is the opposite of #login_required decorator for Django views?

If I want to make sure that a view is listed as having public access, is there a decorator equivalent to #public_access which would be the opposite of #login_required and make it clear that the view should be publicly accessible always?
One use case I have in mind is to automatically add "#csrf_exempt" to all public views in addition to making it clear in the code that the view should be publicly accessible.
Unfortunately, there's currently no built-in support for this in Django, leaving you at risk of exposing sensitive info when #login_required is accidentally forgotten.
Here's a solution from one of my projects:
middleware/security.py:
def public(function):
"""
Decorator for public views that do not require authentication
"""
orig_func = function
while isinstance(orig_func, partial): # if partial - use original function for authorization
orig_func = orig_func.func
orig_func.is_public_view = True
return function
def is_public(function):
try: # cache is found
return function.is_public_view
except AttributeError: # cache is not found
result = function.__module__.startswith('django.') and not function.__module__.startswith('django.views.generic') # Avoid modifying admin and other built-in views
try: # try to recreate cache
function.is_public_view = result
except AttributeError:
pass
return result
class NonpublicMiddleware(object):
def process_view_check_logged(self, request, view_func, view_args, view_kwargs):
return
def process_view(self, request, view_func, view_args, view_kwargs):
while isinstance(view_func, partial): # if partial - use original function for authorization
view_func = view_func.func
request.public = is_public(view_func)
if not is_public(view_func):
if request.user.is_authenticated(): # only extended checks are needed
return self.process_view_check_logged(request, view_func, view_args, view_kwargs)
return self.redirect_to_login(request.get_full_path()) # => login page
def redirect_to_login(self, original_target, login_url=settings.LOGIN_URL):
return HttpResponseRedirect("%s?%s=%s" % (login_url, REDIRECT_FIELD_NAME, urlquote(original_target)))
settings.py:
MIDDLEWARE_CLASSES = (
#...
'middleware.security.NonpublicProfilefullMiddleware',
#...
)
and, finally, view code:
from <projname>.middleware import publi
#public
def some_view(request):
#...
# Login required is added automatically
def some_private_view(request):
#...
Also, you may want to look at "Automatically decorating all views of a django project" blog post
As a previous poster mentioned, login not required is the default.
However, sometimes it's useful to block certain views from logged in users -- for instance, it makes no sense for a logged-in user to be able to use the site's signup page. In that case, you could do something like this, based off the existing login_required decorator
from django.contrib.auth.decorators import user_passes_test
from django.conf import settings
LOGGED_IN_HOME = settings.LOGGED_IN_HOME
def login_forbidden(function=None, redirect_field_name=None, redirect_to=LOGGED_IN_HOME):
"""
Decorator for views that checks that the user is NOT logged in, redirecting
to the homepage if necessary.
"""
actual_decorator = user_passes_test(
lambda u: not u.is_authenticated(),
login_url=redirect_to,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator
A bit late, but another simple way to tackle this issue would be to rely on another decorator and add a lambda function:
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda u: u.is_anonymous)
You can use user_passes_test and add a requirement, anonymous_required. This would work like the opposite to login_required and you can use on your register and login page - it is a bit irritating for users to still see the login page, after the are logged in.
This is how you would do it:
from django.contrib.auth.decorators import user_passes_test
#Anonymous required
def anonymous_required(function=None, redirect_url=None):
if not redirect_url:
redirect_url = settings.LOGIN_REDIRECT_URL
actual_decorator = user_passes_test(
lambda u: u.is_anonymous,
login_url=redirect_url
)
if function:
return actual_decorator(function)
return actual_decorator
#Application of the Decorator
#anonymous_required
def register(request):
#Your code goes here
"Login not required" is the default. If you want to annotate that a view should never be login-restricted then you should do so in the docstring.
I use django-decorator-include to use the login_required decorator to secure an entire app, rather than one view at a time. My app's main urls.py looks like this:
path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),
This works great, except for when one of my apps has one view which needs to be public.
To get around this, I wrote my own login_required and login_not_required. My login_required is based on django's django.contrib.auth.decorators.login_required, but is slightly modified to actually care when a view is marked as not requiring login.
My project is called mysite.
My app is called my_secret_app.
My public view within my_secret_app is called MyPublicView.
My entire solution looks like this:
mysite/lib.py
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test
# A copy of django.contrib.auth.decorators.login_required that looks for login_not_required attr
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
actual_decorator = user_passes_test(
lambda u: u.is_authenticated,
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
login_req = getattr(function, "login_required", True)
if login_req:
return actual_decorator(function)
else:
return function
else:
return actual_decorator
# Decorator to mark a view as not requiring login to access
def login_not_required(f):
f.login_required = False
return f
mysite/urls.py
from .lib import login_required
path('my_secret_app/', decorator_include(login_required, ('my_secret_app.urls', 'my_secret_app'))),
my_secret_app/views.py:
from django.utils.decorators import method_decorator
from mysite.lib import login_not_required
class MyPublicView(View):
#method_decorator(login_not_required)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request):
...
def post(self, request, *args, **kwargs):
...
You should be able to do the same thing no matter if you're subclassing View, ListView, CreateView, UpdateView, TemplateView, or any of the other ones. It should also work if you're using a function as your view, though I haven't tried it:
#login_not_required
def MyPublicView(request):
...
#permission_classes([permissions.AllowAny])