Redirect from get_queryset() in a class view?

785 views
Skip to first unread message

Eli Criffield

unread,
Dec 10, 2011, 1:51:34 PM12/10/11
to django...@googlegroups.com

So in a class based view inheriting generic.ListView I want to redirect on a condition, The logical place to do it is get_queryset, but you can't go returning a HttpResponseRedirect from a method that should return a query set. The django.shortcuts.redirect() just kinda does that for you so that doesn't work either. I can raise a  Http404 from inside get_queryset, but not something like raise Redirect('url/').

I saw one answer to the problem where you put the condition in render_to_response but that'll gets called after get_queryset. I guess the other option is to put the condition in get and check there and return redirect. But that seems hacky.

I'm just wonder whats the best way to do this.

Eli Criffield

Dan Gentry

unread,
Dec 12, 2011, 11:33:41 AM12/12/11
to Django users
I faced a similar situation where I had to check for valid input data
used to build the query in get_queryset(). My solution was to use a
decorator around the dispatch() function, but I think it could also be
used within that function. I chose dispatch() because it acts like a
traditional view function - takes a request and outputs a response.
My code is a little specific to my situation, but it may help. Here is
it:

First, the decorator:

def require_institution():
"""
Decorator to check for a valid institution id in the session
variables
and return institution object in kwargs
"""
def decorator(func):
def inner(request, *args, **kwargs):
## If there is no inst_id set in the session, transfer to
institution select
## page for a chance to select one
try:
institution =
Institution.objects.get(pk=request.session.get('inst_id',None))
kwargs['institution'] = institution
except Institution.DoesNotExist:
path = urlquote(request.get_full_path())
tup = reverse('select_inst'), REDIRECT_FIELD_NAME,
path
return HttpResponseRedirect('%s?%s=%s' % tup)
return func(request, *args, **kwargs)
return wraps(func, assigned=available_attrs(func))(inner)
return decorator

And then the class:

class ListViewInstCheck(ListView):
model = Event
paginate_by = DEFAULT_PAGINATION

@method_decorator(require_institution())
def dispatch(self, request, *args, **kwargs):
return super(ListViewInstCheck, self).dispatch(request, *args,
**kwargs)

def get_queryset(self):
queryset = super(ListViewInstCheck,self).get_queryset()
return queryset.filter(institution=self.kwargs['institution'])

Hope this helps.

Dan

Nan

unread,
Dec 12, 2011, 12:00:18 PM12/12/11
to Django users

I'd recommend against doing that, as it's a strong violation of MVC
principles. Instead you should either catch the
ModelName.DoesNotExist exception in a custom (or inherited, or generic-
view-wrapping) view, or raise a custom exception in your get_queryset
method and catch that exception instead. Then you can return the
HttpResponseRedirect from your catch clause.

On Dec 10, 1:51 pm, Eli Criffield <elicriffi...@gmail.com> wrote:

Eli Criffield

unread,
Dec 12, 2011, 1:50:47 PM12/12/11
to django...@googlegroups.com
I like the raise exception idea, but where do i catch it? 
In a decorator?
Overwrite get() ?

Nan

unread,
Dec 12, 2011, 2:29:43 PM12/12/11
to Django users

I'm not sure, not having explored CBVs yet, but yes, I suspect you'll
want to either decorate or override the method that usually returns
the HttpResponse.
Reply all
Reply to author
Forward
0 new messages