'Book' object is not iterable

Hello!
I have an error when I created DetailView. But I don’t know how to solve this.
I’m trying to build a website for the library.
My Traceback

nvironment:


Request Method: GET
Request URL: http://127.0.0.1:8000/books/1/

Django Version: 3.2.9
Python Version: 3.9.7
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'library']
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 /Users/nikita/Desktop/library/templates/base.html, error at line 6
   'Book' object is not iterable
   1 : {% load static %}
   2 : <html lang="en">
   3 :   <head>
   4 :     <!-- Required meta tags -->
   5 :     <meta charset="utf-8" />
   6 :     <meta name="viewport" c ontent="width=device-width, initia l-scale=1" />
   7 : 
   8 :     <!-- Bootstrap CSS -->
   9 :     <link
   10 :       href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
   11 :       rel="stylesheet"
   12 :       integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
   13 :       crossorigin="anonymous"
   14 :     />
   15 : 
   16 :     <!-- Custom CSS -->


Traceback (most recent call last):
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/core/handlers/base.py", line 204, in _get_response
    response = response.render()
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/response.py", line 83, in rendered_content
    return template.render(context, self._request)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 170, in render
    return self._render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 162, in _render
    return self.nodelist.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 162, in _render
    return self.nodelist.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 938, in render
    bit = node.render_annotated(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/base.py", line 905, in render_annotated
    return self.render(context)
  File "/Users/nikita/.local/share/virtualenvs/library-jU9c7Gmt/lib/python3.9/site-packages/django/template/defaulttags.py", line 167, in render
    values = list(values)

Exception Type: TypeError at /books/1/
Exception Value: 'Book' object is not iterable

Models.py

from django.db import models
from django.urls import reverse
import uuid


class Genre(models.Model):
   name = models.CharField(max_length=100, help_text="Введите название")

   def __str__(self):
      return self.name

class Book(models.Model):
   title = models.CharField(max_length=200)
   image = models.ImageField(upload_to="photos", verbose_name="Фото", default=None, blank=True, null=True)
   author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
   summary = models.TextField(help_text="Введите описание")
   isbn = models.CharField(max_length=10, unique=True, blank=True, null=True)
   genre = models.ManyToManyField('Genre')

   def __str__(self):
      return self.title
      
   def get_absolute_url(self):
      return reverse('book_detail', args=[str(self.id)])


class BookInstance(models.Model):
   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.PROTECT, null=True)
   imprint = models.CharField(max_length=200)
   due_back = models.DateTimeField(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):
      return f' {self.id} ({self.book.title})'

class Author(models.Model):
   first_name = models.CharField(max_length=50)
   last_name = models.CharField(max_length=50)
   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):
      #   return reverse('author-detail', args=[str(self.id)])

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

   

Views.py

from django.shortcuts import render
from django.views.generic import ListView, TemplateView, DetailView, CreateView
from .models import Book, Genre
from .forms import CreateForm

# Create your views here.

class HomeView(ListView):
   model = Book
   template_name = 'home.html'

class BooksView(ListView):
   model = Book
   template_name = 'books.html'

   def get_context_data(self, **kwargs):
      context = super().get_context_data(**kwargs)
      context['genre'] = Genre.objects.all()
      return context

class BookDetailView(DetailView):
   model = Book
   template_name = 'book-detail.html'
   context_object_name = 'book_list'
   

class AboutView(TemplateView):
   template_name = 'about.html'


class BookCreatePage(CreateView):
   model = Book
   template_name = 'create_page.html'
   form_class = CreateForm
   success_url = '/books'



Books.html

from django.shortcuts import render
from django.views.generic import ListView, TemplateView, DetailView, CreateView
from .models import Book, Genre
from .forms import CreateForm

# Create your views here.

class HomeView(ListView):
   model = Book
   template_name = 'home.html'

class BooksView(ListView):
   model = Book
   template_name = 'books.html'

   def get_context_data(self, **kwargs):
      context = super().get_context_data(**kwargs)
      context['genre'] = Genre.objects.all()
      return context

class BookDetailView(DetailView):
   model = Book
   template_name = 'book-detail.html'
   context_object_name = 'book_list'
   

class AboutView(TemplateView):
   template_name = 'about.html'


class BookCreatePage(CreateView):
   model = Book
   template_name = 'create_page.html'
   form_class = CreateForm
   success_url = '/books'



book-detail.html

{% extends 'base.html' %} {% block content %}

<div class="container">
  <div class="row">
    <div class="col">
      <ul>
        {% for book_detail in book_list %}
        <li>{{book_detail.title}}</li>
        <li></li>
        {% endfor %}
      </ul>
    </div>
  </div>
</div>

{% endblock content %}

This line tries to iterate over a variable called book_list.

However BookDetailView puts a single book into the context, in the variable name you give it:

I would suggest using context_object_name = 'book', then in your template don’t use a for loop, but refer to the single book in the variable book only. e.g.

{% extends 'base.html' %} {% block content %}

<div class="container">
  <div class="row">
    <div class="col">
      <h1>{{ book.title }}</h1>
    </div>
  </div>
</div>

{% endblock content %}
1 Like

Adam, thank you so much!
It works!
Can you explain why it works? It will be helpful for me.
Also, can you help me with my next question, how to build link to ListVew to DetailView?
I used in book list, but it doesn’t work for me

            <h5 class="card-tittle fs-6">
              <a href="{{ book.get_absolute_url }}">{{books_list.title}}</a>
            </h5>
            <p class="card-text fs-6">
              <small class="text-muted"> {{books_list.author}} </small>
            </p>
          </div>