Towards a more friendly NoReverseMatch

83 views
Skip to first unread message

Wilfred Hughes

unread,
Oct 12, 2011, 7:56:18 AM10/12/11
to Django developers
It would be really good if we could improve the errors provided when
Django can't do reverse().

For example:

>>> reverse('i_dont_exist')
NoReverseMatch: Reverse for 'i_dont_exist' with arguments '()' and
keyword arguments '{}' not found.

In this case, it would be nicer to have something like:

NoURLPattern: No patterns specified for 'i_dont_exist'.

Alternatively, if NoReverseMatch listed the patterns it tried (similar
to template loader post-mortem given on TemplateDoesNotExist which
shows the folders tried) then a new error message would be
unnecessary:

NoReverseMatch: Reverse for 'i_dont_exist' with arguments '()' and
keyword arguments '{}' not found (patterns tried: []).

As for regex patterns that can't be reversed, it would nice to improve
the errors or improve the docs. The docs state that:

> The main restriction at the moment is that the pattern cannot contain alternative choices using the vertical bar ("|") character.

Upon reading this, I expected that Django would warn me if I tried to
reverse a pattern with a '|' in it. However, it's sometimes possible

url(r'^fruit/(bananas|apples)$', some_view, name='fruit'),

>>> reverse('fruit', args=['bananas'])
'/fruit/bananas'

There's also the issue that there are other ways of expressing
alternatives that produce NoReverseMatch, although the docs don't
really say that these are problematic:

url(r'^fruit(/location/(?P<location>[a-z]+))?(/name/(?
P<function>[a-z]+)/)?$', some_view,
name='fruit_with_alternative_groups'),

>>> reverse('fruit_with_alternative_groups', kwargs={'location':
'london', 'name': 'apples'})
NoReverseMatch: Reverse for 'fruit_with_alternative_groups' with
arguments '()' and keyword arguments '{'location': 'london', 'name':
'apples'}' not found.

It would be great for the error to mention that I've used '?' which
stopped reversing working. Sure, I could have multiple URL patterns
named 'fruit_with_alternative_groups', but if there's only one (or all
the patterns have this problem) then we could be more helpful.

TL;DR:

1. It'd be nice to know how many patterns were tried by reverse().
2. It'd be nice to get a warning when users try to reverse patterns
that contain alternatives with '?' or '?' (or others).
3. It'd be good to clarify the docs as to what patterns are
reversible.

Thoughts? I'll open bugs, whip up a patch or two if people are
interested.

Tom Evans

unread,
Oct 12, 2011, 10:23:18 AM10/12/11
to django-d...@googlegroups.com
On Wed, Oct 12, 2011 at 12:56 PM, Wilfred Hughes
<wil...@potatolondon.com> wrote:
> It would be really good if we could improve the errors provided when
> Django can't do reverse().
>
> For example:
>
>    >>> reverse('i_dont_exist')
>    NoReverseMatch: Reverse for 'i_dont_exist' with arguments '()' and
> keyword arguments '{}' not found.
>
> In this case, it would be nicer to have something like:
>
>    NoURLPattern: No patterns specified for 'i_dont_exist'.
>

Just on this point, I must disagree. For instance, consider if you
have a URL named 'i_dont_exist', and the URL pattern has two
positional arguments. If I attempted to reverse() that name, but don't
supply the required arguments, then the existing error clearly
explains why no match was found - there were no arguments passed.
The proposed error message would not explain why, and would be
misleading - there is a URL pattern, but it is not applicable without
additional arguments.

The most common time I encounter this is when using {% url %} in a
template. You pass in the appropriate positional/named arguments, but
if the variables you pass in don't correspond to valid variables, you
don't want to be told that the reason it couldn't reverse the URL was
because there was no such pattern.

Cheers

Tom

Wilfred Hughes

unread,
Oct 12, 2011, 11:13:14 AM10/12/11
to Django developers
> >    >>> reverse('i_dont_exist')
> >    NoReverseMatch: Reverse for 'i_dont_exist' with arguments '()' and
> > keyword arguments '{}' not found.
>
> > In this case, it would be nicer to have something like:
>
> >    NoURLPattern: No patterns specified for 'i_dont_exist'.
>
> Just on this point, I must disagree. For instance, consider if you
> have a URL named 'i_dont_exist', and the URL pattern has two
> positional arguments.

Ah, sorry. I've been unclear. My point here is that when there /isn't/
a URL with that name. It would be good to distinguish between having
no regexes and not being able to reverse the regex.

So, if I have an URL:

url(r'^fruit/(bananas|apples)$', some_view, name='fruit'),

And I make a spelling mistake:

>>> reverse('rfuit', args=['bananas'])

I would like some hint that the problem isn't in my regex. The two
options I'm proposing are:

NoURLPattern: No patterns specified for 'rfuit'.

Or:

NoReverseMatch: Reverse for 'rfuit' with arguments '()' and
keyword arguments '{}' not found (patterns tried: []).

Philippe Raoult

unread,
Oct 13, 2011, 11:27:34 AM10/13/11
to django-d...@googlegroups.com
+1 on this, been bitten repeatedly.

I assume the url parser already "knows" this so this is mostly a
matter of raising the appropriate exception.

Bien cordialement,
Philippe

> --
> You received this message because you are subscribed to the Google Groups "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to django-develop...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
>
>

Wilfred Hughes

unread,
Oct 19, 2011, 2:20:31 PM10/19/11
to Django developers
I've opened a ticket for improving the NoReverseMatch error:
https://code.djangoproject.com/ticket/17076

However, I'd still like to improve the documentation regarding which
patterns can be reversed. I don't fully understand the limitations
myself yet.

1. Can we provide an example of a pattern containing "|" that doesn't
work? I've successfully reversed the pattern r'^fruit/(bananas|apples)
$' above.

2. Can we give a more precise description of the limits of reversible
patterns? Is it just that the arguments / keyword arguments must match
up (looking at https://github.com/django/django/blob/master/django/core/urlresolvers.py#L364
this seems to be only obvious limitation)?

Tom Evans

unread,
Oct 20, 2011, 6:54:56 AM10/20/11
to django-d...@googlegroups.com
On Wed, Oct 19, 2011 at 7:20 PM, Wilfred Hughes
<wil...@potatolondon.com> wrote:
> 1. Can we provide an example of a pattern containing "|" that doesn't
> work? I've successfully reversed the pattern r'^fruit/(bananas|apples)
> $' above.

Any regexp with alternation that is not part of a captured parameter:

url(r'^homepage/(?:apple|banana)$', 'homepage', name='bad_homepage')
url(r'^homepage/a|b$', 'homepage', name='bad_homepage2')


Cheers

Tom

Reply all
Reply to author
Forward
0 new messages