Reputation: 47
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:
try:
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})
else:
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:
Environment:
Request Method: GET
Request URL: http://localhost:8081/movies/movie/Twilight/
Django Version: 1.10.1
Python Version: 3.5.2
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todolist.apps.TodolistConfig',
'moviesrating.apps.MoviesratingConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
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 :
Traceback:
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\\ ]+)/$']
Upvotes: 3
Views: 805
Reputation: 418
You can add parentheses around capturing groups:
urlpatterns = [
url(r'^movie/((?P<movie_id>\d+)|(?P<movie_name>[a-zA-Z\ ]+))/$', view_movie, name = 'movie-details'),
]
and add defaults to view parameters:
def view_movie(request, movie_id=None, movie_name=None):
if movie_id:
return HttpResponse('1')
if movie_name:
return HttpResponse('2')
Idk why but i've tried this on my project and it works.
Upvotes: 0
Reputation: 5151
You are only sending one parameter to your view, though it expects two. If you want to stick with one that can be interpreted as an id or a name, why not accept an alpha-numeric parameter
urlpatterns = [
url(r'^movie/(?P<movie_id>[A-z0-9]+)/$', view_movie, name = 'movie-details'),
]
and then check in your view which one it is so you know if you should treat it as an id or a name
def view_movie(request, movie_id):
try:
int(movie_id)
movie_name = False
except:
movie_name = True
if not movie_name:
movie = get_object_or_404(Movie, pk = movie_id)
elif movie_name:
# etc.
Upvotes: 0
Reputation: 53669
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):
...
Upvotes: 1