0

As per Django 3 changelogs:

RegexPattern, used by re_path(), no longer returns keyword arguments with None values to be passed to the view for the optional named groups that are missing.

Recently upgraded from Django 2.2 to 3.2 after which I'm facing an issue for which I suspect the above-mentioned changelog.

The issue is I get KeyError while accessing the URL parameters as keyword arguments in the view (using get_context_data(...)) when accessing URLpatterns that are defined using re_path().

FYI, just to verify I rolled back to Django 2.2 and checked on the context data from the view and could see that the required Key in the self.kwargs dict was set to None

Is there any way to return keyword arguments with None values to the views in Django 3.2?

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Somraj Chowdhury
  • 983
  • 1
  • 6
  • 14

1 Answers1

0

From what I understand you have a pattern of the form, r'(?P<group>pattern)?' i.e. a capturing group that you make optional, in which case normally it would return None if it doesn't match anything.

If you really want to do this rather than refactoring your code, you can inherit from RegexPattern and override match by copying it from the source code [GitHub]:

from django.urls.conf import _path
from django.urls.resolvers import RegexPattern
from functools import partial


class MyRegexPattern(RegexPattern):
    def match(self, path):
        match = self.regex.search(path)
        if match:
            # If there are any named groups, use those as kwargs, ignoring
            # non-named groups. Otherwise, pass all non-named arguments as
            # positional arguments.
            kwargs = match.groupdict()
            args = () if kwargs else match.groups()
            # Commented line below from source code
            # kwargs = {k: v for k, v in kwargs.items() if v is not None}
            return path[match.end():], args, kwargs
        return None


# Make the re_path function
re_path = partial(_path, Pattern=MyRegexPattern)

Rather than using this workaround though the better solution would be to either specify multiple url patterns instead of one or fix your views to allow such situations.

Suppose you had patterns like:

re_path(r'^prefix(?P<group>pattern)?suffix/$', views.some_view),

You can write two patterns like:

re_path(r'^prefix(?P<group>pattern)suffix/$', views.some_view),
re_path(r'^prefixsuffix/$', views.some_view, kwargs={'group': None}),

In case you previously had a function based view like:

def some_view(request, group):
    ...

Simply change it so that group has a default value of None:

def some_view(request, group=None):
    ...

If in class based views you were writing self.kwargs['group'] instead write self.kwargs.get('group').

Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33