I am a bit puzzled. I want to check if the current url of the website is in a specific subset of my urlpatters. Which function of django would do this? This is for a middleware that requests additional information before moving to a specific part of my website.
class AuthorityMiddleware(object):
def process_request(self, request):
current_url = request.path_info
if not request.user.is_anonymous() and current_url not in exclude_list:
if not request.session.get('authority'):
return get_the_authority(request)
How should exclude list relate to the urlpatterns defined in urls.py?
Ok, my issue basically is the result of bad design. The whole issue can be solved if I use decorators to ask for the authority for the views that require it. Back when I wrote the above code I was thinking that middlewares would be the only place to redirect and call further functions before a view is shown, but in fact they are good to apply a function to all your views. Decorators are better if the functions only apply to a subset of views.
from django.core.urlresolvers import reverse, resolve
from django.http import HttpResponseRedirect
def project_required(func):
def decorated(request, *args, **kwargs):
if not request.session.get('authority'):
request.session['previous_url'] = resolve(request.path_info).url_name
return HttpResponseRedirect(reverse('project_login'))
return func(request, *args, **kwargs)
return decorated
Related
I am many views and more than one user type. I want some views that can be seen by specific user types and other users cant see this.
For example, only company see this views and for this i did that like this below:
#login_required
def only_company_can_view(request):
if not Company.objects.filter(owner_id=request.user.id).exists():
return HttpResponse('Permission Denied. Only Company can see this')
# > rest of the logic
return render(request, 'template.html')
and above this working very well and solves my problem but i don't like this. coz, i don't want to write every time for the rest of the views for the company related views.
So i am finding a solution so that i can use decorator or something else that are best practice
Can anyone help me in this case?
You can wrap the logic in a decorator:
from django.core.exceptions import PermissionDenied
from functools import wraps
def requires_company(view):
#wraps(view)
def _view(request, *args, **kwargs):
if not Company.objects.filter(owner_id=request.user.id).exists():
raise PermissionDenied
return view(request, *args, **kwargs)
return _view
Then you use the decorator with:
#login_required
#requires_company
def only_company_can_view(request):
# … rest of the logic …
return render(request, 'template.html')
I've developed my own, but it seems like it's a great enough thing that someone else probably thought of it first and did a better job ;)
The goal is to be able to write, in your myapp/views.py
router = Router(prefix=r'^myapp/')
#router.route(url=r'my/url/here', name="my-great-view")
def myview(request):
return render_to_response("mytemplate.html", {})
And then in urls.py
urlpatterns += myapp.views.router.get_patterns()
And there are several other cool decorator things I've built in (#jsonview for taking a returned dictionary and making a json response, #use_template for taking a returned dictionary and passing it to the defined template as the context...)
It seems like this way makes everything a lot more localized an readable. When looking at a view function you don't have to search around to find what url it belongs to, or what it's "named".
I saw this one djangosnippet, but it's a lot more magic than I'd like, and it doesn't look like it's been generally adopted.
In the event that no one has put together a standard solution, should I clean mine up and submit a pull request to contrib?
here is what I currently have implemented: magic.py
Edit:
if you want multiple urls for the same view:
#router.route(url="my-first-url", kwargs={'foo':'bar'})
#router.route(url="my-second=url", kwargs={'foo':'baz'})
def my_view(...): ...
And of course this doesn't have to be the only way to do it -- the normal urlpatterns way has some nice things about it two, but these two methods are not mutually exclusive.
The regular configuration we have a main website urls.py . And the urls.py contains variable named urlpatterns.
so wo can push some url_pattern into it.
app/views.py
from django.urls import path as djpath
URLS = []
URLS_d = {}
def route(path=''):
def wrapper(func):
path_name = path or func.__name__
URLS.append(
djpath(path_name, func)
)
### below is not important ####
path_func = URLS_d.get(path_name, None)
if path_func:
print(path_func, '<>', func)
raise Exception('THE same path')
URLS_d[path_name] = func
### above is not important ####
return wrapper
#route()
def index(req):
return HttpResponse('hello')
website/urls.py
from app.views import URLS
urlpatterns.extend(URLS)
If you use class base view , you can use django-request-mapping,
A Simple Example
view.py
from django_request_mapping import request_mapping
#request_mapping("/user")
class UserView(View):
#request_mapping("/login/", method="post")
def login(self, request, *args, **kwargs):
return HttpResponse("ok")
#request_mapping("/signup/", method="post")
def register(self, request, *args, **kwargs):
return HttpResponse("ok")
#request_mapping("/<int:user_id>/role/")
def get_role(self, request, user_id):
return HttpResponse("ok")
#request_mapping("/<int:pk/", method='delete')
def delete(self, request, pk):
User.objects.filter(pk=pk).delete()
return HttpResponse("ok")
#request_mapping("/role")
class RoleView(View):
# ...
urls.py
from django_request_mapping import UrlPattern
urlpatterns = UrlPattern()
urlpatterns.register(UserView)
urlpatterns.register(RoleView)
and request urls are:
post: http://localhost:8000/user/login/
post: http://localhost:8000/user/signup/
get: http://localhost:8000/user/1/role/
delete: http://localhost:8000/user/1/
# ...
I am using django-tokenapi to allow for authentication of an Android project that is using Django as a backend. The project also has a web interface.
django-tokenapi uses the #token_required decorator to protect certain views. Django uses the #login_required decorator to protect certain views.
What I would like is to have only one view that is protected by #login_required OR #token_required so it can be used with either the webapp or Android app.
So, ideally, it would look like this:
#token_required
#login_required
def names_update(request):
....
....
However that does not work. Is there a better method of doing this? Or is the correct thing to have two views, one the webapp and one for Android, that are protected by the proper decorator, and then lead to the same method.
No there's no easy way, if possible at all, to write an generalized OR decorator according to you described.
However, you can write a new decorator that does exactly what you want:
from functools import wraps
from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
def token_or_login_required(view_func):
"""
Decorator which ensures the user is either logged in or
has provided a correct user and token pair.
"""
#csrf_exempt
#wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
user = request.REQUEST.get('user')
if user and user.is_authenticated:
return view_func(request, *args, **kwargs)
token = request.REQUEST.get('token')
if user and token:
user = authenticate(pk=user, token=token)
if user:
login(request, user)
return view_func(request, *args, **kwargs)
return HttpResponseForbidden()
return _wrapped_view
this decorate combines both token_required and login_required decorators, and will allow access to the view either if the user is logged in, or the token is valid.
You could try assigning a new view variable to the old view:
#token_required
def names_update_token(request):
...
#login_required
names_update_login = names_update_token
This should have the effect of creating a second view named names_update_login which is just a pointer to the first so that the code remains the same.
EDIT:
Another thought, and one I have used, is to create a more "generic" view and call it from each of the other views:
def update_token(request):
...
#token_required
def names_update_token(request):
update_token(request)
#login_required
def names_update_login(request):
update_token(request)
This gets around the issue you mentioned while still maintaining only a single copy of the actual code that implements the view.
It is possible to use more than one decorator on a single view. But in this case I think you should seperate the view and apply the decorators seperately. Otherwise token_required decorator will try to authenticate using token which won't be available on a request made using browser.
I want to redirect access of unauthenticated users to the login page, after which the logged-in user should be redirected to the originally requested page.
According to documentation, this is easily achieved using the #user_passes_test decorator. But it seems I'd have to decorate every view, which is crazy, there are too many and it's error-prone.
What is a good way to turn on this functionality globally (except for a small fixed set of views, such as login)? That is, default everything to logged-in-only + handle anonymous viewing explicitly, where needed.
from django.shortcuts import redirect
from django.conf import settings
class LoginRequiredMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.login_url = settings.LOGIN_URL
self.open_urls = [self.login_url] + \
getattr(settings, 'OPEN_URLS', [])
def __call__(self, request):
if not request.user.is_authenticated \
and not request.path_info in self.open_urls:
return redirect(self.login_url+'?next='+request.path)
return self.get_response(request)
have a look at middleware. these are functions run at various points in the request cycle, e.g. before each view is called.
since you may want to exclude certain views from this, i'd look at e.g. how the csrf middleware works, together with the csrf_exempt decorator.
see [SOURCE]/django/views/decorators/csrf.py and [SOURCE]/django/middleware/csrf.py
The way I solved this, was to have mixin class, with the decorator (or whatever code you need). Although you have to remember to call the super(Class, self).get(...) function, so I guess it's not so different after all.
On the other hand, having a set of mixins that does different things I found was quite good at getting a very simple view to do a lot without much code.
Edit
This is how I did in my last project:
class BaseAuthMixin(object):
def auth_check(self, user):
return True
def dispatch(self, request, *args, **kwargs):
if not self.auth_check(request.user):
from django.http import HttpResponseRedirect
from django.contrib.auth import logout
is_web = False
is_live = False
if hasattr(self, 'get_url_name'):
from django.core.urlresolvers import reverse
from django.core.urlresolvers import NoReverseMatch
try:
next = reverse(self.get_url_name(), kwargs=kwargs)
except NoReverseMatch:
next = ''
else:
next= '?next=' + next
logout(request)
redirect_url = settings.LOGIN_URL
redirect_url += next
return HttpResponseRedirect(redirect_url)
else:
return super(BaseAuthMixin, self).dispatch(request, *args, **kwargs)
class LoginRequiredMixin(BaseAuthMixin):
"""
Check if the view needs the user to be logged in.
"""
def auth_check(self, user):
if not super(LoginRequiredMixin, self).auth_check(user):
return False
else:
if hasattr(self, 'login_required'):
if self.login_required and not user.is_authenticated():
return False
return True
class MyDefaultMixin(LoginRequiredMixin):
"""
Mixin that inherits from all common view mixins.
"""
pass
The above is then used by the view-classes (I used Django 1.3 with class-based views):
from django.views.generic import TemplateView
class SomeViewClass(TemplateView, MyDefaultMixin):
# Used by 'LoginRequiredMixin' to check if a user has to be logged in
login_required = True
# Template for the Django TemplateView
template_name = "some_view_template.html"
You need a view to handle the login (with URL in settings.LOGIN_URL), containing a form with a hidden field called next. This field has to be set by a context variable to the page to go to after successful login.
If all views inherit from the base mixin (MyDefaultMixin in my code above), it will automatically check that the user is logged in iv the view contain an attribute called login_required and that is set to True.
There might be better ways to do this, but this is what I did and it worked very well.
So I have an app called stats that lets me query my database in various ways and return information in a JSON format so I can have a nice ajaxy dashboard for graphing and visualizing. I'd like this app to be as reusable as possible, naturally, so I don't want to necesarily use the #login_required decorator on its views. In my case, however, I do want a login to be required before viewing any of the apps views. Is there a way to do this somewhere other than the views?
Perhaps something like this in my site's urls.py? (I know this won't work, an example of what I'm looking for)
urlpatterns = patterns('',
(r'^stat/', include('stats.urls'), login_required),
)
You can apply decorator for individual urls in urls.py in this manner:
from django.contrib.auth.decorators import login_required
import views
(r'^stat/', login_required(views.index))
you can use a middleware for that
here is example snippet - http://www.djangosnippets.org/snippets/1179/
you can use this snippet and define LOGIN_EXEMPT_URLS in your settings or modifiy it a little bit for your case
If you're concerned about reusability, rather than using login_required, you could use a decorator which requires login if a particular argument is passed to the view (which might default to a value of True). Off the top of my head, it might look a little like this:
from django.contrib.auth.decorators import login_required
def login_possibly_required(view_func):
def inner(request, *args, **kwargs):
try:
require_login = kwargs.pop('require_login')
if require_login:
return login_required(view_func)(request, *args, **kwargs)
except KeyError:
pass
return view_func(request, *args, **kwargs)
return inner
Then you'd define your views like so:
#login_possibly_required
my_view(request, arg1, arg2, require_login=True):
pass
Not tested, but you get the idea.