Login

Generic object_detail view with multiple named URL filters

Author:
cotton
Posted:
December 14, 2011
Language:
Python
Version:
1.3
Score:
0 (after 0 ratings)

This snippet is greatly inspired by @jlorich's useful #2436.

The main difference is that I wanted to choose the names of my URL params instead of being forced into naming them "value1", "value2", etc. When reversing the URL you have to remember that the kwargs aren't friendly. By using the same names in the filters list, you don't have to change the way your otherwise write the URL pattern. Also it's clear throughout how you'll be filtering the QuerySet.

The other change I made was "erroring early". This avoids running the QuerySet all over again inside object_detail() just to have it raise an exception we could have caught the first time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from django.http import Http404
from django.views.generic.list_detail import object_detail
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist

def filtered_object_detail(request, queryset, filters=[], **kwargs):
    """
    Generic object-detail view that can filter a QuerySet
    on multiple named URL parameters

    This generic view will filter a QuerySet on any number of
    named URL parameters and will "error early". It expects a
    single object from the QuerySet and will raise exceptions
    without having to step into object_detail(), which will
    ultimately run the QuerySet again and raise the same
    exceptions.

    Usage:
        The named URL parameters must match the filter list.

        url(r'^(?P<blog__slug>[-\w]+)/(?P<slug>[-\w]+)/$', filtered_object_detail, {
                'queryset': BlogPost.objects.all(),
                'filters': ['blog__slug', 'slug'],
            },
        name='blog-post'),

    """
    args = {}

    for item in filters:
        if item in kwargs.keys():
            args[item] = kwargs.pop(item)
        else:
            raise AttributeError("filtered_object_detail view must used all "\
                                 "the given `filters` as named URL kwargs.")

    queryset = queryset.filter(**args)

    try:
        object_id = queryset.get().id
    except MultipleObjectsReturned:
        raise MultipleObjectsReturned("filtered_object_detail view is "\
                                      "designed to return a single object")
    except ObjectDoesNotExist:
        raise Http404("No %s found matching the query" \
                      % (queryset.model._meta.verbose_name))

    return object_detail(request, queryset, object_id, **kwargs)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

bluerat (on February 20, 2012):

Thanks, very useful :-)

#

Please login first before commenting.