views:

203

answers:

2

So, I have this Django application and I keep adding new features to provide ever more granular views of the data. To give a quick idea of the problem, here's a subset of urls.py:

# Simple enough . . .
(r'^$', 'index'),
(r'^date/(?P<year>\d{4})$', 'index'),
(r'^date/(?P<year>\d{4})-(?P<month>\d{2})$', 'index'),
(r'^date/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', 'index'),
(r'^page/(?P<page>\d+)$', 'index'),

So, yeah, the default view, date views, a paginated view, then a similar setup for user-specific URLs:

# user
(r'^user/(?P<username>\w+)$', 'index_username'),
(r'^user/(?P<username>\w+)/date/(?P<year>\d{4})$', 'index_username'),
(r'^user/(?P<username>\w+)/date/(?P<year>\d{4})-(?P<month>\d{2})$', 'index_username'),
(r'^user/(?P<username>\w+)/date/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', 'index_username'),
(r'^user/(?P<username>\w+)/page/(?P<page>\d+)$', 'index_username'),

Then, similar again for Teams . . .

# Team View
(r'^team/(?P<team>\w+)$', 'index'),
(r'^team/(?P<team>\w+)/date/(?P<year>\d{4})$', 'index'),
(r'^team/(?P<team>\w+)/date/(?P<year>\d{4})-(?P<month>\d{2})$', 'index'),
(r'^team/(?P<team>\w+)/date/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', 'index'),
(r'^team/(?P<team>\w+)/page/(?P<page>\d+)$', 'index'),

Now, I want to add a "search" view, and really I think it'd be nice to just add that to the end of many of the above URLs, so I could hit something like:

/search/foo
/user/fred/date/2010-01/search/baz

And then I could just pass a search parameter to the view, which could constrains the results appropriately.

But say, if I want to apply that to the user, team, and by-date views, I've got what 12 new lines added to urls.py. And each time I add another potential view option (say, paginated search results?) . . . it just feels like there ought to be a better way.

From my research, the answer would seem to be:
1) Broader matching within urls.py and have the view function parse the query string.
2) Maybe some beefier regular expression logic in urls.py that can say "if this pattern matches, include the parameter when passing to the view function" multiple times. But that's possibly nightmarish to maintain.

Has anyone conjured a wiser solution to managing complex URLs elegantly? I'm thinking that at a certain point it is cleanest simply to pass the parameter-matching logic to the view itself, to parse parameters out of the query string. So, near the top of the view I might have some code that looks like this:

# Date Queries
re_ymd = re.compile('date/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})')
re_ym = re.compile('date/(?P<year>\d{4})-(?P<month>\d{2})')
re_y = re.compile('date/(?P<year>\d{4})')
if( re_ymd.search(qs) ):
    year = re_ymd.search(qs).group('year')
    month = re_ymd.search(qs).group('month')
    day = re_ymd.search(qs).group('day')
elif( re_ym.search(qs) ):
    year = re_ym.search(qs).group('year')
    month = re_ym.search(qs).group('month')
elif( re_y.search(qs) ):
    year = re_y.search(qs).group('year')

# Pagination queries
re_p = re.compile('page/(?P<page>\d+)')
if( re_p.search(qs) ):
    page = re_p.search(qs).group('page')

# Search queries
re_s = re.compile('search/(?P<search>\w+)')
if( re_s.search(qs) ):
    search = re_s.search(qs).group('search')

So, is that the clever way of reducing the repetetive complexity I have been introducing to urls.py?

+1  A: 

Why not use GET parameters, or django-filter, if all you do is just filter/group the results differently?

The reasons I see for using GET are that its easier to implement, and seems a little cleaner: in the URL solution /search/foo/user/bar/ and /user/bar/search/foo/ are 2 names for the exact same content. In the GET parameters solution its all the same page.

Ofri Raviv
You have some valid point here - URL is meant for uniquely identifying resources, not for filtering.
Tomasz Zielinski
Yeah, that's what I'm getting at near the bottom of my question: pretty much bypassing `urls.py` and parsing out the query arguments from the request string within the view function. I'm not sure that django-filter is the tool I want, but thank you for affirming the solution that I perceive . . .
dannyman
But in your solution, you're still using URLs, just not using django's URL routing mechanism, but inventing your own. using GET params your code will be half as long, and twice as clear.
Ofri Raviv
A: 

Let's focus on this one:

    # Simple enough . . .
    (r'^$', 'index'),
    (r'^date/(?P<year>\d{4})$', 'index'),
    (r'^date/(?P<year>\d{4})-(?P<month>\d{2})$', 'index'),
    (r'^date/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', 'index'),
    (r'^page/(?P<page>\d+)$', 'index'),

I assume that there is some:

    def index(year=2010, month=2, day=2, page=0):
        # whatever

So, why don't you combine you regexes in one, e.g.

     r'^date/(?P<year>\d{4})(-(?P<month>\d{2})(-(?P<day>\d{2}))?)?$

I haven't tried it, but I'm pretty sure something like this would work.

EDIT:

While re-writing regexes could work, take a look at Ofri Raviv's answer because it could be that you have some meta-problem here.

Tomasz Zielinski