Problem when doing a search with multiple results. NoReverseMatch

I am making a web application that uses the OpenLibrary API, using Django. In it I do a search within the API, if the results show more than one book, I get this error:

NoReverseMatch at /search-results/

Reverse for 'book_detail' with arguments '('reina-roja-red-queen', 'Reina Roja / Red Queen')' not found. 1 pattern(s) tried: ['books/((?P<slug>[-a-zA-Z0-9_]+)/(?P<title>[^/]+)+)']

My files are as follows:

urls.py

from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("search-results/", views.search_results, name="search_results"),
    path("books/<slug:slug>/<str:title>", views.book_detail, name="book_detail"),
]

views.py

from django.shortcuts import render, get_object_or_404, redirect
from django.utils.text import slugify
from books.api_library import search_book_by_author, search_book_by_title
from .models import Book
import requests
import json

def index(request):
    """
    Index page
    """
    if request.method == "POST":
        type_search = request.POST.get("type_search")
        search = request.POST.get("search")

        if type_search == "title":
            books_data = search_book_by_title(search).get("docs", [])
        elif type_search == "author":
            books_data = search_book_by_author(search).get("docs", [])
        
        if not books_data:
            books_data = []

        
        request.session['search_results'] = books_data

        
        return redirect('search_results')

    return render(request, "books/index.html")

def search_results(request):
    """
    View to display search results
    """
    
    search_results = request.session.get('search_results', [])

    books = []
    for book in search_results:
        slug = slugify(book.get("title", ""))

        book_info = {
            "title": book.get("title", ""),
            "author": book.get("author_name", ""),
            "isbn": book.get("isbn", ""),
            "cover": book.get("cover", ""),
            "publisher": book.get("publisher", ""),
            "cover_url": f"https://covers.openlibrary.org/b/id/{book.get('cover_i', '')}-S.jpg",
            "description": book.get("description", ""),
            "publish_date": book.get("publish_date", ""),
            "slug": slug
        }
        books.append(book_info)

    return render(request, "books/search_results.html", {"books": books})

def book_detail(request, slug, title):
    """
    Detail page
    """
    print(title)
    url = f"https://openlibrary.org/search.json?title={title}"
    response = requests.get(url)
    book_data = response.json()
    print(book_data)

    for book in book_data:

        context = {
            "title": book.get("title", ""),
            "author": book.get("author_name", ""),
            "isbn": book.get("isbn", ""),
            "cover": book.get("cover", ""),
            "publisher": book.get("publisher", ""),
            "cover_url": f"https://covers.openlibrary.org/b/id/{book.get('cover_i', '')}-L.jpg",
            "description": book.get("description", ""),
            "publish_date": book.get("publish_date", ""),
        }

    return render(request, "books/book_detail.html", context)

index.html

{% extends "base.html" %}


{% block content %}
<form method="post">
    {% csrf_token %}
    <article class="main__article search">
        <fieldset class="search__fieldset">
            <p class="search__p">
                <label for="search" class="search__label">Search</label>
                <select name="type_search" id="type_search">
                    <option value="title">Title</option>
                    <option value="author">Author</option>
                </select>
                <input type="text" name="search" id="search" class="search__input">
                <button type="submit" class="search__button">Search</button>
            </p>
    </article>
</form>
<br>
<article class="card">
    {% for book in books %}
    <figure class="card__figure">
        <img src="{{ book.cover_url }}" class="card__img", alt="{{ book.title }}", height="200", width="200">
    </figure>
    <div class="card__div">
        <h2 class="card__h2">{{ book.title }}</h2>
        <p class="card__p">Author: {{ book.author }}</p>
        <p class="card__p">Publish date: {{ book.publish_date }}</p>
        <p class="card__p">Publisher: {{ book.publisher }}</p>
        <a href="{% url 'book_detail' book.slug book.isbn %}" class="card__a" title="{{ book.title }}'}">View More</a>
    </div>
    {% endfor %}
</article>

{% endblock %}

search_results.html

{% extends "base.html" %}

{% block content %}
<h1>Search Results</h1>

    {% if books %}
        <ul>
            {% for book in books %}
                <li>
                    <a href="{% url 'book_detail' book.slug book.title %}">{{ book.title }}</a> by {{ book.author }}
                </li>
            {% endfor %}
        </ul>
    {% else %}
        <p>No results found.</p>
    {% endif %}

{% endblock %}

book_detail.html

{% extends "base.html" %}

{% block content %}
<article class="book-details">
    <h1>{{ title }}</h1>
    <p><strong>Author(s):</strong> {{ author }}</p>
    {% if cover_url %}
    <img src="{{ cover_url }}" alt="Book Cover">
    {% endif %}
    <p><strong>Description:</strong> {{ description }}</p>
    <!-- Otros detalles del libro que desees mostrar -->
</article>
{% endblock %}

I want to be shown all the results and to be able to choose a book and to be taken to see in book_detail.html the data of the book.

Your second parameter contains a slash, a <str:…> does not allow that, you should use the <path:…> path converter:

path('books/<slug:slug>/<path:title>', views.book_detail, name='book_detail'),

That being said, you are passing a title with spaces, slashes, etc. That is often not a good idea, especially with respect to Search Engine Optimization (SEO).

Back to Top