urls with object - django

I have element in my urls.py
url(r'^name/(?P<some_id>.+)/$', Class(), name='name'),
Is it possible somehow to receive some_id in class?

When you pass a class instance as view, its __call__ method will be executed. The parameters passed are the same as in function-based views (except the self as first argument). Text that you capture in your regex will be passed as string to the function/method. (Capturing text in urls)
class A(object):
def __call__(self, request, some_id):
# stuff
If you don't already know this, Django will use class-based views (besides the function-based) as of version 1.3. http://docs.djangoproject.com/en/dev/ref/class-based-views/

You can do this with decorator.
def accepts_some_object(f):
def new_f(request, some_object_id, *args, **kwargs):
some_object = get_object_or_404(...)
return f(request, *args, **kwargs, some_object=some_object)
return new_f
#accepts_some_object
def some_view(request, some_object)
Useful if you have many views which accept some_object.

Related

Django: is_secure() decorator not working

I'm converting some FBVs with signals to CBVs, so I have this decorator:
def ensure_https(view_func):
def _checkssl(request, *args, **kwargs):
print request.is_secure()
if not settings.DEBUG and not request.is_secure():
url_str = request.build_absolute_uri()
url_str = url_str.replace('http://', 'https://')
return HttpResponseRedirect(url_str)
return view_func(request, *args, **kwargs)
return _checkssl
and added it to a function in a class based view, as so:
class ExampleTemplateView(TemplateView):
template_name = 'example.html'
#ensure_https
def dispatch(self, request, *args, **kwargs):
...
return HttpResponseRedirect(/hello/')
But I get the following error:
'ExampleTemplateView' object has no attribute 'is_secure'
However, when I use this decorator on a function-based view, it works just fine. Should I be using a particular CBV?
If you need anymore code or info, please let me know. Thanks for your help!
I think you have signal and decorator confused as the pattern in your code is a decorator. Depending on what you're doing there might be better alternatives to where you put the URL redirection logic. I'm thinking webserver(nginx), HTTP Strict Transport Security HTTP header or middleware. Having said that, from the django docs:
To decorate every instance of a class-based view, you need to decorate
the class definition itself. To do this you apply the decorator to the
dispatch() method of the class.
A method on a class isn’t quite the same as a standalone function, so
you can’t just apply a function decorator to the method – you need to
transform it into a method decorator first. The method_decorator
decorator transforms a function decorator into a method decorator so
that it can be used on an instance method. For example:
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)

Django, let decorator value be available in view function

I have a decorator:
def site_login_required(function=None):
def _dec(view_func):
def _view(request, *args, **kwargs):
gets site_id. checks stuff. if else.
This is called in the normal way:
#site_login_required
def frontPage(request, url_arg):
The decorator finds a site_id. It would be nice to have this available in the frontPage function. Is that possible? If so how?
You could pass it in as a kwarg when you call the view function within the decorator:
def site_login_required(function=None):
def _dec(view_func):
def _view(request, *args, **kwargs):
site_id = get_site_id()
url_arg = get_a_url()
view_func(request, url_arg, site_id=site_id)
#site_login_required
def frontPage(request, url_arg, site_id=None, *args, **kwargs):
...
As a side note, it is useful to accept *args and **kwargs in all view functions, so they consume extra arguments instead of choking with decorators like this wrapped around them.

Get request.session from a class-based generic view

Is there a way to get request.session from inside a class-based view?
For instance, I have
from django.views.generic.edit import FormView
class CreateProfileView(FormView):
def form_valid(self, form):
# --> would like to save form contents to session here
return redirect(self.get_success_url())
The only thing I can think of would be override as_view by adding
def as_view(self, request, *args, **kwargs):
self.session = request.session
super(CreateProfileView, self).as_view(request, *args, **kwargs)
to the class. But that seems ugly. Is there another way?
You have access to self.request from anywhere within the class (and therefore self.request.session)
https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-display/#dynamic-filtering
The key part to making this work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request) this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.

Redirect from Generic View DetailView in Django

I'm using Django's class based DetailView generic view to look up an object for display. Under certain circumstances, rather than displaying the object, I wish to back out and issue a HTTP rediect instead. I can't see how I go about doing this. It's for when a user hits an object in my app, but without using the canonical URL. So, for example, on StackOverflow URLs take the form:
http://stackoverflow.com/<content_type>/<pk>/<seo_friendly_slug>
eg:
http://stackoverflow.com/questions/5661806/django-debug-toolbar-with-django-cms-and-django-1-3
You can actually type anything as the seo_friendly_slug part and it will redirect you to the correct canonical URL for the object looked up via the PK.
I wish to do the same in my DetailView. Retrieve the object, check that it's the canonical URL, and if not redirect to the item's get_absolute_url URL.
I can't return an HttpResponseRedirect in get_object, as it's expecting the looked up object. I can't seem to return it from get_context_data, as it's just expecting context data.
Maybe I just need to write a manual view, but I wondered if anyone knew if it was possible?
Thanks!
Ludo.
This isn't a natural fit for DetailView. To do this you need to override the get method of BaseDetailView, which looks like:
class BaseDetailView(SingleObjectMixin, View):
def get(self, request, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
So in your class you'd need to provide a new get method which did the URL check between fetching the object and setting up the context. Something like:
def get(self, request, **kwargs):
self.object = self.get_object()
if self.request.path != self.object.get_absolute_url():
return HttpResponseRedirect(self.object.get_absolute_url())
else:
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
As you end up overriding so much of the functionality it becomes questionable whether it's worth actually using a generic view for this, but youknow.
Developing on Rolo's answer and comments, I came up with the following generic view to serve this purpose:
from django import http
from django.views import generic
class CanonicalDetailView(generic.DetailView):
"""
A DetailView which redirects to the absolute_url, if necessary.
"""
def get_object(self, *args, **kwargs):
# Return any previously-cached object
if getattr(self, 'object', None):
return self.object
return super(CanonicalDetailView, self).get_object(*args, **kwargs)
def get(self, *args, **kwargs):
# Make sure to use the canonical URL
self.object = self.get_object()
obj_url = self.object.get_absolute_url()
if self.request.path != obj_url:
return http.HttpResponsePermanentRedirect(obj_url)
return super(CanonicalDetailView, self).get(*args, **kwargs);
This is used in the same manner as the normal DetailView, and should work for any model which implements get_absolute_url correctly.

Wrapping/decorating a function in urls.py vs in views.py

So, I'm pretty familiar with wrapping functions in views.py. So I've written a decorator to redirect to the default REDIRECT_URL if the user is logged in (sort of a reverse login_required); it's based on how I've made views in the past:
def not_logged_in(redirect_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
def decorator(view_func, *args, **kwargs):
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated():
return view_func(*args, **kwargs)
else:
redirect_url = (request.REQUEST.get(redirect_field_name, redirect_url) or
settings.REDIRECT_URL)
return HttpResponseRedirect(redirect_url)
return wrapper
return decorator
However, I get the following error: 'function' object has no attribute 'status_code' which is caused by a MiddleWare expecting an HttpResponse. When I look at the value for response, I see that it's <function wrapper at 0x2b3a9922a500>.
Here's how I'm calling it in urls.py:
url(r'login/',
not_logged_in(auth_views.login),
{'authentication_form': LoginForm },
),
Here’s my implementation of the same thing.
def logout_required(view):
def f(request, *args, **kwargs):
if request.user.is_anonymous():
return view(request, *args, **kwargs)
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL)
return f
In urls.py:
urlpatterns = patterns("",
url(r"^login/", logout_required(login), {"template_name": "users/login.html"}, "login"),
# ...
I hope this helps (unsure though).
The first argument to a decorator should be the function that needs to be decorated.
def not_logged_in(func, redirect_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
The decorator function is also not needed. Return the wrapper function from not_logged_in.
The way you've implemented your decorator, it is parameterised and therefore callable: that's why you've got the extra level of function that fizixx wrongly says is not required. You need to call the outer wrapper initially, in order to return the actual decorated function. So something like:
url(r'login/',
not_logged_in(auth_views.login)('/redirect/', 'redirect_field'),
{'authentication_form': LoginForm },
),