Rishipal Singh
Rishipal Singh

Reputation: 44

Page rendering with model.py but not working after data is added in the models through admin portal

I am working on MDN Django tutorial on LocalLibrary. I am using get_absolute_url to render generic ListView and DetailView page. App is rendering page with else statement i.e. There are no books in the library. But when I add books in the library through admin portal in my models. Same pages are not rendering and reflecting below error

NoReverseMatch at /catalog/books/
Reverse for 'book-detail' not found. 'book-detail' is not a valid view function or pattern name.

Please find the models.py as follows

import uuid # Required for unique book instances
from django.db import models

# Create your models here.
# Used to generate URLs by reversing the URL patterns
from django.urls import reverse

class Genre(models.Model):
    """Model representing a book genre."""
    name = models.CharField(
        max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')

    def __str__(self):
        """String for representing the Model object."""
        return self.name

class Language(models.Model):
    name = models.CharField(max_length=200, help_text='Enter a book language(e.g. English)')
    
    def __str__(self):
        return self.name


class Book(models.Model):
    """Model representing a book (but not a specific copy of a book)."""
    title = models.CharField(max_length=200)

    # Foreign Key used because book can only have one author, but authors can have multiple books
    # Author as a string rather than object because it hasn't been declared yet in the file
    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)

    summary = models.TextField(
        max_length=1000, help_text='Enter a brief description of the book')
    isbn = models.CharField('ISBN', max_length=13, unique=True,
                            help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')

    # ManyToManyField used because genre can contain many books. Books can cover many genres.
    # Genre class has already been defined so we can specify the object above.
    genre = models.ManyToManyField(
        Genre, help_text='Select a genre for this book')
    
    language = models.ManyToManyField(
        Language, help_text='Select Language for this book')
    
    def __str__(self):
        """String for representing the Model object."""
        return self.title

    def get_absolute_url(self):
        """Returns the url to access a detail record for this book."""
        return reverse('book-detail', kwargs= {'pk': str(self.id)})
    
    def display_genre(self):
        """Create a string for the Genre. This is required to display genre in Admin."""
        return ', '.join(genre.name for genre in self.genre.all()[:3])

    display_genre.short_description = 'Genre'

    def get_language(self):
        """Create a string for the Genre. This is required to display genre in Admin."""
        return ', '.join(language.name for language in self.language.all()[:3])

    get_language.short_description = 'Language'
    

class BookInstance(models.Model):
    """Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
    id = models.UUIDField(primary_key=True, default=uuid.uuid4,
                          help_text='Unique ID for this particular book across whole library')
    book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
    imprint = models.IntegerField()
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    status = models.CharField(
        max_length=1,
        choices=LOAN_STATUS,
        blank=True,
        default='m',
        help_text='Book availability',
    )

    class Meta:
        ordering = ['due_back']

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.id} ({self.book.title})'


class Author(models.Model):
    """Model representing an author."""
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    class Meta:
        ordering = ['last_name', 'first_name']

    def get_absolute_url(self):
        from django.urls import reverse
        """Returns the url to access a particular author instance."""
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.last_name}, {self.first_name}'

Attached is the urls.py

from django.urls import path

from catalog import views
from catalog.views import BookListView, BookDetailView

app_name = 'catalog'

urlpatterns = [
    path('', views.index, name='index'),
    path('books/', views.BookListView.as_view(), name='books'),
    path('book/<int:pk>', views.BookDetailView.as_view(), name='book-detail'),
    path('authors/', views.AuthorListView.as_view(), name='authors'),
    path('author/<int:pk>', views.AuthorDetailView.as_view(), name='author-detail'),
]
  1. Views.py
from django.shortcuts import get_object_or_404
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from django.http import HttpResponse
from django.shortcuts import render
from catalog.models import *

def index(request):
    num_books = Book.objects.all().count()
    num_instances = BookInstance.objects.all().count()
    num_genre = Genre.objects.all()
    num_instances_available = BookInstance.objects.filter(status__exact='avail').count()
    num_authors = Author.objects.count()
    
    context = {
        'num_books': num_books,
        'num_instances': num_instances,
        'num_instances_available': num_instances_available,
        'num_authors': num_authors,
        'num_genre': num_genre,
    }
   
    return render(request, 'catalog/index.html', context=context)

class BookListView(ListView):
    model = Book
    paginate_by = 2

class BookDetailView(DetailView):
    model = Book
    
class AuthorListView(ListView):
    model = Author
    
class AuthorDetailView(DetailView):
    model = Author
  1. Template of book_list.html This is saved as per this method catalog/templates/catalog/book_list.html
{% extends "catalog/base.html" %}

{% block title %}
    <title>List of Books</title>
{% endblock %}

{% block content %}
    <h1>Book List</h1>

    {% if book_list %}
      <ul>

        {% for book in book_list %}
        <li>
          <a href="{{ book.get_absolute_url }}">{{ book.title }}</a>
          <a href="{{ author.get_absolute_url }}">({{ book.author }})</a> 
        </li>
        {% endfor %}
      </ul>
      
    {% else %}
      <p>There are no books in the library.</p>
    {% endif %}       
{% endblock %}
  1. book_detail.html This is saved as per this method catalog/templates/catalog/book_detail.html

{% block content %}

    <h1>Title: {{ book.title }}</h1>

    <p><strong>Author:</strong> <a href="{{ book.author.get_absolute_url }}">{{ book.author }}</a></p>
    <p><strong>Summary:</strong> {{ book.summary }}</p>
    <p><strong>ISBN:</strong> {{ book.isbn }}</p> 
    <p><strong>Language:</strong> {{ book.language }}</p>  
    <p><strong>Genre:</strong> {{ book.genre.all|join:", " }}</p>

    <div style="margin-left:20px;margin-top:20px">
        <h4>Copies</h4>

    {% for copy in book.bookinstance_set.all %}
        <hr>
        <p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'd' %}text-danger{% else %}text-warning{% endif %}">{{ copy.get_status_display }}</p>
    {% if copy.status != 'a' %}<p><strong>Due to be returned:</strong> {{copy.due_back}}</p>{% endif %}
    <p><strong>Imprint:</strong> {{copy.imprint}}</p>
    <p class="text-muted"><strong>Id:</strong> {{copy.id}}</p>

    {% endfor %}
    </div>
{% endblock %}```

I also find error on the same page for bootstrap link which is getting extended from base file and working fine with index.html


whereas on books.html page
It is showing this error
In template /Users/rishipalsingh/Projects/notes/mdndjango/mdn/catalog/templates/catalog/base.html, error at line 7

base.html
``` Thank you

Upvotes: 0

Views: 72

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21806

You have namespaced your urls by writing:

app_name = 'catalog'

Hence reverse('book-detail', ...) will not work and you need to specify the namespace along with the url name, hence it should be:

reverse('catalog:book-detail', kwargs= {'pk': str(self.id)})

Similarly for the author:

reverse('catalog:author-detail', args=[str(self.id)])

Upvotes: 1

Related Questions