An interesting URL dispatcher problem

10 views
Skip to first unread message

redmonkey

unread,
Nov 4, 2008, 11:50:12 AM11/4/08
to Django users
Hi,

I have a small form on a page that is mostly optional fields with the
exception of some drop down boxes. I want to take the data submitted
through this form and use them to form a URL.

My problem is with the URL writing. I first wrote some unit tests to
find the regular expressions that worked but Django doesn't seem to
like the '?' in URL configurations.

How can I write a workflow that converts optional, POSTed data to a
RESTful URL for processing?

Here's an example:

class IceCreamForm(forms.form):
description = forms.CharField(required=False)
flavour = forms.ChoiceField(choices=FLAVOUR_CHOICES, initial=0)
FLAVOUR_CHOICES = (
...
)

This is my form class. It contains one optional field, and one
required field. When rendered on a page, the form POSTs it's data to
the the 'process' view:

def process(request):
# Standard form stuff (is_valid) ...
params = {}
if form.cleaned_data['description']:
params.update(description=form.cleaned_data['description'])
if form.cleaned_data['flavour']:
params.update(flavour=form.cleaned_data['flavour'])

return HttpResponseRedirect('proj.app.views.filter', kwags=params)

def filter(request, description=None, flavour=0):
... do stuff ...

Here, I collect the parameters from the form and try to turn them into
a pretty URL that redirects to the filter view to do something with
the data. It's the URL writing I'm having problems with. I want to
write things like:

urlpatterns += patterns('proj.app.views',
(r'^(?P<flavour>\w+)/((?P<description>\w+)/)?$', 'filter'),
)

The question mark is wrapped around an extra '/' to complete the URL,
but it doesn't work. Nor does

urlpatterns += patterns('proj.app.views',
(r'^(?P<flavour>\w+)/$', 'filter'),
(r'^(?P<flavour>\w+)/(?P<description>\w+/$', 'filter'),
)

I'm surprised with the result of the last one, and so I'm sure I'm
missing something. Does anyone have any ideas or simplifications for
me?

Thanks,

Red Monkey


TiNo

unread,
Nov 4, 2008, 12:13:13 PM11/4/08
to django...@googlegroups.com

Here, I collect the parameters from the form and try to turn them into
a pretty URL that redirects to the filter view to do something with
the data. It's the URL writing I'm having problems with. I want to
write things like:

Why not do it in the process view immediatly?


urlpatterns += patterns('proj.app.views',
   (r'^(?P<flavour>\w+)/((?P<description>\w+)/)?$', 'filter'),
)

The question mark is wrapped around an extra '/' to complete the URL,
but it doesn't work. Nor does

urlpatterns += patterns('proj.app.views',
   (r'^(?P<flavour>\w+)/$', 'filter'),
   (r'^(?P<flavour>\w+)/(?P<description>\w+/$', 'filter'),
)

you are missing a closing ) on the last line:
 (r'^(?P<flavour>\w+)/(?P<description>\w+)/$', 'filter'),

Tino

Steve Holden

unread,
Nov 4, 2008, 12:23:04 PM11/4/08
to django...@googlegroups.com
It might be better to try something that makes the last slash optional
in a different way. How about

(r'^(?P<flavour>\w+)(/(?P<description>\w+))/?$', 'filter'),

> The question mark is wrapped around an extra '/' to complete the URL,
> but it doesn't work. Nor does
>
> urlpatterns += patterns('proj.app.views',
> (r'^(?P<flavour>\w+)/$', 'filter'),
> (r'^(?P<flavour>\w+)/(?P<description>\w+/$', 'filter'),
> )
>
> I'm surprised with the result of the last one, and so I'm sure I'm
> missing something. Does anyone have any ideas or simplifications for
> me?
>

The last one doesn't even appear to be a syntactically correct re due to
mismatching parentheses ... did you copy and paste, or mis-transcribe?

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
Holden Web LLC http://www.holdenweb.com/

Brian Neal

unread,
Nov 4, 2008, 12:33:53 PM11/4/08
to Django users
On Nov 4, 10:50 am, redmonkey <michele.mem...@googlemail.com> wrote:
>
> My problem is with the URL writing. I first wrote some unit tests to
> find the regular expressions that worked but Django doesn't seem to
> like the '?' in URL configurations.

? is a special character in regular expressions, just like $, ^, etc.
You must escape it if you want to use it literally, e.g. \?. However,
I don't think the ? as part of any GET arguments in a URL are going to
be available to you, from what I remember about django works.

I'm not really following what you are trying to do however.

redmonkey

unread,
Nov 4, 2008, 12:42:27 PM11/4/08
to Django users
I have to admit, the missing ) is a typo. I made up the ice cream
example to try to simplify my problem, and accidentally missed it off.

Perhaps my example is too simple.
> I have a small form on a page that is mostly optional fields with the
> exception of some drop down boxes.

My actual form has 3 optional fields, and this complicated matters
with matching URLs. Fortunately, two of the fields are limits for a
price search, so I've used "-" to identify the re, and the third is a
text description so I'm expecting URLs that look like "/100-200/persian
%20rug/" (Where 100-200 is the price range, and "persian%20rug" is a
quoted text search).

My issue is that any of those 3 fields may not be submitted, but
somehow I need to redirect to the process view.

The <a href="http://jobs.guardian.co.uk/jobs/" title="Guardian
Jobs">Guardian Jobs</a> site is a nice example of the effect I want to
create as you add filtering paramters on the left, although I'm using
a form to collect user input as opposed to giving them a list of
ranges which is more difficult.

I hope that clears a few things up.

redmonkey

unread,
Nov 4, 2008, 12:48:28 PM11/4/08
to Django users
Hi Brian,

Thanks for your comment. I completely understand what you are saying,
and anyone trying to match the "?" character should take note, but in
this situation I am using it to try to tell Django that that named
group in the regular expression may, or may not, be included.

RM

redmonkey

unread,
Nov 7, 2008, 10:22:47 AM11/7/08
to Django users
Here's my solutions, for anyone who may be experiencing the same
problem.

In my url config, I have 4 different named patterns corresponding to
each of the possibilities:

url(r'^(?P<site>[a-zA-Z]+)/(?P<sort_by>[\+\-].\w+)/$',
'catalogueFilter', name="catalogue_search_no_filters"),
url(r'^(?P<site>[a-zA-Z]+)/(?P<sort_by>[\+\-].\w+)/(?
P<min_estimate>\d+(?=-))?-(?P<max_estimate>(?<=-)\d+)?/$',
'catalogueFilter', name="catalogue_search_estimates"),
url(r'^(?P<site>[a-zA-Z]+)/(?P<sort_by>[\+\-].\w+)/(?
P<description>[a-zA-Z0-9%]+)/$', 'catalogueFilter',
name="catalogue_search_desc"),
url(r'^(?P<site>[a-zA-Z]+)/(?P<sort_by>[\+\-].\w+)/(?
P<min_estimate>\d+(?=-))?-(?P<max_estimate>(?<=-)\d+)?/(?
P<description>[a-zA-Z0-9%]+)/$', 'catalogueFilter',
name="catalogue_search_estimates_desc"),

I apologise for them being a bit messy, but you can see that each
pattern starts with "(?P<site>[a-zA-Z]+)/(?P<sort_by>[\+\-].\w+)"
These correspond to the drop down boxes in my form. They will always
be submitted. The first pattern contains just these parameters, and so
the url is called "catalogue_search_no_filters".

Second, we have a pattern for if some estimates are submitted. These
are optional form fields that can be filled in if the user wants to
narrow down the search. In order to allow for this flexibility, there
are a lot of '?' in the regular expression. The regular expression
adds a '-' character in to represent a range of values.

We then have another url for just the optional description field. This
is a simple text search.

Finally I have a named url for the optional estimate fields and an
optional description field. I was trying to write one single url that
allowed for both, but I couldn't. Instead I came up with this
solution. I different named url that points to the same view function.

The view function itself is pretty straight forward. It accepts all
the parameters and includes so default values:

def catalogueFilter(request, site='any', min_estimate=None,
max_estimate=None,
sort_by=LotSearchForm.SORT_CHOICES_URLS[0][1], description=None):

.... do stuff, get "results' together ...

return object_list(
request=request,
queryset=results,
page=page,
paginate_by=20,
template_object_name="lot",
extra_context=dict(
search_form=form
)
)

I'm using a generic view to return the HttpResponse.

The messy bit is how I determine which named url to call. I'm afriad
it's a lot of if/else statements branching off to different
HttpResponseRedirects:

if ("min_estimate" in params) or ("max_estimate" in params):
if "description" in params:
return HttpResponseRedirect(
reverse('catalogue_search_estimates_desc', kwargs=params))
else:
return HttpResponseRedirect(
reverse('catalogue_search_estimates', kwargs=params)
else:
if "description" in params:
return HttpResponseRedirect(
reverse('catalogue_search_desc', kwargs=params))
else:
return HttpResponseRedirect(
reverse('catalogue_search_no_filters', kwargs=params))

I admit this isn't elegant at all, but it works. If anyone has any
ideas on how I could improve this, please let me know. If nothing
else, it might clear up what I'm trying to do here.

Thanks,

RM
Reply all
Reply to author
Forward
0 new messages