worm worm - 1 year ago 106
Python Question

Django error: NoReverseMatch

I'm using Django 1.10 and python 3.4

The precise error is

NoReverseMatch at /movies/movie/Twilight/
Reverse for 'movie-details' with arguments '(8,)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['movies/movie/(?P<movie_id>\\d+)|(?P<movie_name>[a-zA-Z\\ ]+)/$']

The error is caused by this line:
{% url 'moviesrating:movie-details' movie.id %}
in template moviesrating/select_movie.html

In file moviesrating/urls.py, which is correctly included in the main urls file, there are those lines:

app_name = 'moviesrating'

urlpatterns = [
url(r'^movie/(?P<movie_id>\d+)|(?P<movie_name>[a-zA-Z\ ]+)/$', view_movie, name = 'movie-details'),

which refers to function view_movie in moviesrating/views.py:

def view_movie(request, movie_id, movie_name):
if movie_id:
movie = get_object_or_404(Movie, pk = movie_id)
elif movie_name:
movie = get_object_or_404(Movie, name = movie_name)
except MultipleObjectsReturned: # There are two movies named 'Twilight'
movies = get_list_or_404(Movie, name = movie_name)
return render(request, "moviesrating/select_movie.html", {'movies': movies})
movie = None
return render(request, "moviesrating/movie.html", {'movie': movie})

The purpose of the url /movies/movie/... is to show a movie found by name or by id, the specific url pattern comes from this need.

The point is that the error shows that even if it doesn't find the reverse match it finds the right url pattern so I thought the pattern didn't match. Then I tried to change the line to:

{% url 'moviesrating:movie-details' movie.id %}
{% url 'moviesrating:movie-details' movie_id=movie.id movie_name=None %}
{% url 'moviesrating:movie-details' movie.id None %}
{% url 'moviesrating.views.view_movie' movie.id %}
{% url 'moviesrating.views.view_movie' movie_id=movie.id %}
{% url 'moviesrating.views.view_movie' movie_id=movie.id movie_name=None %}

but none of them worked.

Viewing the link in the browser (ex. http://localhost:8081/movies/movie/8) works, so it's not that page that causes the problem.

At this point really I can't understand the problem, I've followed the passages explained in the django docs and read a lot of questions already asked but none of them solved my problem.

If something is unclear or more information is needed please let me know and I will edit the post. Thanks

Here is the full stacktrace of the error:


Request Method: GET
Request URL: http://localhost:8081/movies/movie/Twilight/

Django Version: 1.10.1
Python Version: 3.5.2
Installed Applications:
Installed Middleware:

Template error:
In template C:\Users\fra\Programmazione\Python\myserver\moviesrating\templates\moviesrating\select_movie.html, error at line 12
Reverse for 'movie-details' with arguments '(8,)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['movies/movie/(?P<movie_id>\\d+)|(?P<movie_name>[a-zA-Z\\ ]+)/$'] 2 : <html>
3 : <head>
4 : <meta charset="ISO-8859-1">
5 : <title>Choice your movie</title>
6 : </head>
7 : <body>
8 : <h2>Choice of which movie named {{ movies.0.name }} would you see the details</h2>
9 :
10 : <ul>
11 : {% for movie in movies %}
12 : <li> {% url 'moviesrating:movie-details' movie.id %} </li>
13 : <li><a href="{{ movie_url }}">{{ movie.name }} del {{ movie.year }} diretto da {{ movie.director }}</a></li>
14 : {% endfor %}
15 : </ul>
16 : </body>
17 : </html>
18 :


File "C:\Users\fra\Programmazione\Python\myserver\moviesrating\views.py" in view_movie
31. movie = get_object_or_404(Movie, name = movie_name)

File "C:\Program Files (x86)\Python\lib\site-packages\django\shortcuts.py" in get_object_or_404
85. return queryset.get(*args, **kwargs)

File "C:\Program Files (x86)\Python\lib\site-packages\django\db\models\query.py" in get
389. (self.model._meta.object_name, num)

During handling of the above exception (get() returned more than one Movie -- it returned 2!), another exception occurred:

File "C:\Program Files (x86)\Python\lib\site-packages\django\core\handlers\exception.py" in inner
39. response = get_response(request)

File "C:\Program Files (x86)\Python\lib\site-packages\django\core\handlers\base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)

File "C:\Program Files (x86)\Python\lib\site-packages\django\core\handlers\base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\fra\Programmazione\Python\myserver\moviesrating\views.py" in view_movie
34. return render(request, "moviesrating/select_movie.html", {'movies': movies})

File "C:\Program Files (x86)\Python\lib\site-packages\django\shortcuts.py" in render
30. content = loader.render_to_string(template_name, context, request, using=using)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\loader.py" in render_to_string
68. return template.render(context, request)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\backends\django.py" in render
66. return self.template.render(context)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\base.py" in render
208. return self._render(context)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\base.py" in _render
199. return self.nodelist.render(context)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\base.py" in render
994. bit = node.render_annotated(context)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\base.py" in render_annotated
961. return self.render(context)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\defaulttags.py" in render
209. nodelist.append(node.render_annotated(context))

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\base.py" in render_annotated
961. return self.render(context)

File "C:\Program Files (x86)\Python\lib\site-packages\django\template\defaulttags.py" in render
439. url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)

File "C:\Program Files (x86)\Python\lib\site-packages\django\urls\base.py" in reverse
91. return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))

File "C:\Program Files (x86)\Python\lib\site-packages\django\urls\resolvers.py" in _reverse_with_prefix
392. (lookup_view_s, args, kwargs, len(patterns), patterns)

Exception Type: NoReverseMatch at /movies/movie/Twilight/
Exception Value: Reverse for 'movie-details' with arguments '(8,)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['movies/movie/(?P<movie_id>\\d+)|(?P<movie_name>[a-zA-Z\\ ]+)/$']

Answer Source

Django's reverse() cannot handle disjunctive patterns (using a |) outside of a capturing group. It's one of those things you'd hope someone would've fixed somewhere in the past 10 or so years, but this limitation has been around since 1.0.

A workaround is to split up the pattern into two patterns:

urlpatterns = [
    url(r'^movie/(?P<movie_id>\d+)/$', view_movie, name='movie-details'),
    url(r'^movie/(?P<movie_name>[a-zA-Z\ ]+)/$', view_movie, name='movie-details'),

You'll need to add a default to both parameters:

def view_movie(request, movie_id=None, movie_name=None):
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download