1

I'm trying to do catch a missing variable in a Django rotue with middleware - however I am unable to reverse the URL as Django cannot find the view (even though it exists in urlconf). For example:

With this route:

# matches /test and /game/test
url(r'^((?P<game>[A-Za-z0-9]+)/)?test', 'hyp.views.test'), 

I am trying to detect if the game part is not given, and redirect in that case with middleware:

class GameMiddleware:
    def process_view(self, request, view_func, view_args, view_kwargs):
        if 'game' in view_kwargs:
            game = view_kwargs['game']

            if game is None:

                # As a test, attempt to resolve the url
                # Correctly finds ResolverMatch for hyp.views.test, game=TestGame
                print resolve('/TestGame/test', urlconf=request.urlconf)

                # Fails with "Reverse for 'hyp.views.test' with arguments '()'
                # and keyword arguments '{'game': 'TestGame'}' not found."
                return HttpResponseRedirect(reverse(
                    request.resolver_match.url_name, # 'hyp.views.test'
                    urlconf=request.urlconf,
                    kwargs={'game': 'TestGame'}
                ))

        return None

request.urlconf does contain the test url:

{ '__name__': 'urlconf', '__doc__': None, 'urlpatterns': [
    <RegexURLPattern None ^$>,
    <RegexURLPattern None ^((?P<game>[A-Za-z0-9]+)/)?test>
], '__package__': None }

The only thing I can think of is that the URL-rewriter might not be able to deal with the regex containing optional parts - would a better solution be to create separate views for these cases (I'm going to have a lot of views with optional game params) or can I fix it?

Update

I managed to get it to work by removing the wrapping brackets in the route (so it reads r'^(?P<game>[A-Za-z0-9]+/)?test' and by passing 'TestGame/' as the game - however this isn't ideal as I have to call game.rstrip('/') each time (although only in the middleware). It's also difficult to use {% url %} tags as a name ending with / is expected.

Leaving this open in case someone has a better solution.

Ross
  • 46,186
  • 39
  • 120
  • 173

1 Answers1

0

Thanks to Todd's answer on another question, I found a clean method of doing this: define two routes (one with the game and one without), specifying game as None in the route without the pattern:

url(r'^test$', 'hyp.views.test', {'game': None}),
url(r'^(?P<game>[A-Za-z0-9]+)/test', 'hyp.views.test'),

This triggers the middleware's if game is None part correctly and also allows games to be specified without trailing slashes.

Community
  • 1
  • 1
Ross
  • 46,186
  • 39
  • 120
  • 173
  • 2
    I think you can do one up! In your view `def test(request, game=None)` then you should be able to skip the `{'game': None}` in your url conf. I haven't tried it but I think it should work. I'm not a big fan of putting data in the urls conf because then it becomes so much more than a "router" and it starts having logic which will be confusing for new developers to get into. If it ever comes to that. – Henrik Andersson Apr 15 '13 at 09:08