5. Generic list and detail views - LiVanych/locallibrary GitHub Wiki

Read full version of this artictle

Book list page

URL mapping

Open /catalog/urls.py and copy in the line shown below.

urlpatterns = [
    path('', views.index, name='index'),
    path('books/', views.BookListView.as_view(), name='books'),
]

View (class-based)

Open catalog/views.py, and copy the following code into the bottom of the file:

from django.views import generic

class BookListView(generic.ListView):
    model = Book

Creating the List View template

vim catalog/templates/catalog/book_list.html
{% extends "base_generic.html" %}

{% 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> ({{book.author}})
      </li>
    {% endfor %}
  </ul>
  {% else %}
    <p>There are no books in the library.</p>
  {% endif %}       
{% endblock %}

Update the base template

vim catalog/templates/base_generic.html
...
<li><a href="{% url 'index' %}">Home</a></li>
  <li><a href="{% url 'books' %}">All books</a></li>
<li><a href="">All authors</a></li>
...

Book detail page

URL mapping

Open /catalog/urls.py and add the 'book-detail' URL mapper shown in bold below. This path() function defines a pattern, associated generic class-based detail view, and a name.

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'),
]

View (class-based)

Open catalog/views.py, and copy the following code into the bottom of the file:

class BookDetailView(generic.DetailView):
    model = Book

Creating the Detail View template

vim catalog/templates/catalog/book_detail.html
{% extends "base_generic.html" %}

{% block content %}
  <h1>Title: {{ book.title }}</h1>

  <p><strong>Author:</strong>
   <a href="">{{ book.author }}</a>
   </p> <!-- author detail link not yet defined -->
  <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> 
    {% for genre in book.genre.all %} 
     {{ genre }}
     {% if not forloop.last %}, 
     {% endif %}
    {% endfor %}
  </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 == 'm' %}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 %}

Pagination

Views

Open catalog/views.py, and add the paginate_by line shown in bold below.

class BookListView(generic.ListView):
    model = Book
    paginate_by = 10

Templates

Open /locallibrary/catalog/templates/base_generic.html and copy in the following pagination block below our content block (highlighted below in bold). The code first checks if pagination is enabled on the current page. If so then it adds next and previous links as appropriate (and the current page number).

{% block content %}{% endblock %}
  {% block pagination %}
  {% if is_paginated %}
    <div class="pagination">
      <span class="page-links">
        {% if page_obj.has_previous %}
          <a href="
             {{ request.path }}?page={{ page_obj.previous_page_number }}">
              previous
          </a>
        {% endif %}
        <span class="page-current">
          <p>Page {{ page_obj.number }} 
               of {{ page_obj.paginator.num_pages }}.
           </p>
        </span>
        {% if page_obj.has_next %}
          <a href="
             {{ request.path }}?page={{ page_obj.next_page_number }}">
             next
          </a>
        {% endif %}
      </span>
    </div>
  {% endif %}
{% endblock %} 

The page_obj is a Paginator object that will exist if pagination is being used on the current page. It allows you to get all the information about the current page, previous pages, how many pages there are, etc. We use to get the current page URL for creating the pagination links. This is useful, because it is independent of the object that we're paginating.

Thats it!

See also

Built-in class-based generic views (Django docs)

Generic display views (Django docs)

Introduction to class-based views (Django docs)

Built-in template tags and filters (Django docs).

Pagination (Django docs)

Read full version of this artictle

⚠️ **GitHub.com Fallback** ⚠️