Coverage for catalog/views.py: 96.91%
93 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-29 10:34 -0700
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-29 10:34 -0700
1import datetime 1a
2from typing import ClassVar 1a
4from django.contrib.auth.decorators import login_required, permission_required 1a
5from django.contrib.auth.mixins import ( 1a
6 LoginRequiredMixin,
7 PermissionRequiredMixin,
8)
9from django.db.models import Q 1a
10from django.http import HttpResponseRedirect 1a
11from django.shortcuts import get_object_or_404, render 1a
12from django.urls import reverse, reverse_lazy 1a
13from django.views.generic import ( 1a
14 CreateView,
15 DeleteView,
16 DetailView,
17 ListView,
18 UpdateView,
19)
21from .forms import RenewBookForm 1a
22from .models import Author, Book, BookInstance 1a
25def index(request): 1a
26 num_books = Book.objects.all().count() 1a
27 num_instances = BookInstance.objects.all().count() 1a
28 num_instances_available = BookInstance.objects.filter(status__exact="a").count() 1a
29 num_authors = Author.objects.count() 1a
30 num_visits = request.session.get("num_visits", 0) 1a
31 request.session["num_visits"] = num_visits + 1 1a
32 context = { 1a
33 "num_books": num_books,
34 "num_instances": num_instances,
35 "num_instances_available": num_instances_available,
36 "num_authors": num_authors,
37 "num_visits": num_visits,
38 }
40 return render(request, "index.html", context=context) 1a
43class BookListView(ListView): 1a
44 """Generic class-based view for a list of books."""
46 model = Book 1a
47 paginate_by = 10 1a
50class BookDetailView(DetailView): 1a
51 """Generic class-based detail view for a book."""
53 model = Book 1a
56class AuthorListView(ListView): 1a
57 """Generic class-based list view for a list of authors."""
59 model = Author 1a
60 paginate_by = 10 1a
63class AuthorDetailView(DetailView): 1a
64 """Generic class-based detail view for an author."""
66 model = Author 1a
69class LoanedBooksByUserListView(LoginRequiredMixin, ListView): 1a
70 model = BookInstance 1a
71 template_name = "catalog/bookinstance_list_borrowed_user.html" 1a
72 paginate_by = 10 1a
74 def get_queryset(self): 1a
75 return ( 1a
76 BookInstance.objects.filter(borrower=self.request.user)
77 .filter(status__exact="o")
78 .order_by("due_back")
79 )
82class LoanedBooksAllListView(PermissionRequiredMixin, ListView): 1a
83 model = BookInstance 1a
84 permission_required = "catalog.can_mark_returned" 1a
85 template_name = "catalog/bookinstance_list_borrowed_all.html" 1a
86 paginate_by = 10 1a
88 def get_queryset(self): 1a
89 return BookInstance.objects.filter(status__exact="o").order_by("due_back") 1a
92@login_required 1a
93@permission_required("catalog.can_mark_returned", raise_exception=True) 1a
94def renew_book_librarian(request, pk): 1a
95 """View function for renewing a specific BookInstance by librarian."""
96 book_instance = get_object_or_404(BookInstance, pk=pk) 1a
98 # If this is a POST request then process the Form data
99 if request.method == "POST": 1a
100 # Create a form instance and populate it with data
101 # from the request (binding):
102 form = RenewBookForm(request.POST) 1a
104 # Check if the form is valid:
105 if form.is_valid(): 105 ↛ 119line 105 didn't jump to line 119 because the condition on line 105 was always true1a
106 # process the data in form.cleaned_data as required (here we
107 # just write it to the model due_back field)
108 book_instance.due_back = form.cleaned_data["renewal_date"] 1a
109 book_instance.save() 1a
111 # redirect to a new URL:
112 return HttpResponseRedirect(reverse("all-borrowed")) 1a
114 # If this is a GET (or any other method) create the default form
115 else:
116 proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) 1a
117 form = RenewBookForm(initial={"renewal_date": proposed_renewal_date}) 1a
119 context = { 1a
120 "form": form,
121 "book_instance": book_instance,
122 }
124 return render(request, "catalog/book_renew_librarian.html", context) 1a
127class AuthorCreate(PermissionRequiredMixin, CreateView): 1a
128 model = Author 1a
129 fields: ClassVar[list[str]] = ["first_name", "last_name", "date_of_birth", "date_of_death"] 1a
130 initial: ClassVar[dict[str]] = {"date_of_birth": "02/02/1882"} 1a
131 permission_required = "catalog.can_mark_returned" 1a
132 template_name = "catalog/author_create.html" 1a
135class AuthorUpdate(PermissionRequiredMixin, UpdateView): 1a
136 model = Author 1a
137 fields: ClassVar[list[str]] = [ 1a
138 "first_name",
139 "last_name",
140 "middle_name",
141 "date_of_birth",
142 "date_of_death",
143 ]
144 permission_required = "catalog.can_mark_returned" 1a
145 template_name = "catalog/author_update.html" 1a
148class AuthorDelete(PermissionRequiredMixin, DeleteView): 1a
149 model = Author 1a
150 permission_required = "catalog.can_mark_returned" 1a
151 success_url = reverse_lazy("authors") 1a
154class BookCreate(PermissionRequiredMixin, CreateView): 1a
155 model = Book 1a
156 fields: ClassVar[list[str]] = [ 1a
157 "title",
158 "author",
159 "pages",
160 "publisher",
161 "pubdate",
162 "summary",
163 "isbn",
164 "genre",
165 "language",
166 ]
167 permission_required = "catalog.can_mark_returned" 1a
168 template_name = "catalog/book_create.html" 1a
171class BookUpdate(PermissionRequiredMixin, UpdateView): 1a
172 model = Book 1a
173 fields: ClassVar[list[str]] = [ 1a
174 "title",
175 "author",
176 "pages",
177 "publisher",
178 "pubdate",
179 "summary",
180 "isbn",
181 "genre",
182 "language",
183 ]
184 permission_required = "catalog.can_mark_returned" 1a
185 template_name = "catalog/book_update.html" 1a
188class BookDelete(PermissionRequiredMixin, DeleteView): 1a
189 model = Book 1a
190 success_url = reverse_lazy("books") 1a
191 permission_required = "catalog.can_mark_returned" 1a
194class SearchResultsListView(ListView): 1a
195 model = Book 1a
196 context_object_name = "books" 1a
197 template_name = "catalog/search_results.html" 1a
199 def get_queryset(self): 1a
200 query = self.request.GET.get("q")
201 return Book.objects.filter(
202 Q(title__icontains=query)
203 | Q(author__first_name__icontains=query)
204 | Q(author__middle_name__icontains=query)
205 | Q(author__last_name__icontains=query)
206 | Q(language__name__icontains=query)
207 | Q(publisher__icontains=query)
208 | Q(pubdate__icontains=query)
209 | Q(genre__name=query)
210 ).distinct()