diff --git a/catalog/models.py b/catalog/models.py
index f0b461f3..b1e1ef01 100644
--- a/catalog/models.py
+++ b/catalog/models.py
@@ -12,6 +12,10 @@ class Genre(models.Model):
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
+ def get_absolute_url(self):
+ """Returns the url to access a particular genre instance."""
+ return reverse('genre-detail', args=[str(self.id)])
+
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
@@ -22,6 +26,10 @@ class Language(models.Model):
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
+ def get_absolute_url(self):
+ """Returns the url to access a particular language instance."""
+ return reverse('language-detail', args=[str(self.id)])
+
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
@@ -42,7 +50,7 @@ class Book(models.Model):
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
-
+
class Meta:
ordering = ['title', 'author']
@@ -53,7 +61,7 @@ def display_genre(self):
display_genre.short_description = 'Genre'
def get_absolute_url(self):
- """Returns the url to access a particular book instance."""
+ """Returns the url to access a particular book record."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
@@ -99,6 +107,10 @@ class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
+ def get_absolute_url(self):
+ """Returns the url to access a particular book instance."""
+ return reverse('bookinstance-detail', args=[str(self.id)])
+
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
diff --git a/catalog/templates/base_generic.html b/catalog/templates/base_generic.html
index c7a4b8cc..baa8cb14 100644
--- a/catalog/templates/base_generic.html
+++ b/catalog/templates/base_generic.html
@@ -1,13 +1,13 @@
-
+
{% block title %}Local Library {% endblock %}
-
+
{% load static %}
@@ -21,35 +21,46 @@
{% block sidebar %}
-
+
-
+
{% if user.is_staff %}
{% endif %}
-
+
{% endblock %}
{% block content %}{% endblock %}
-
+
{% block pagination %}
{% if is_paginated %}
{% endif %}
- {% endblock %}
-
-
+ {% endblock %}
+
+
diff --git a/catalog/templates/catalog/author_confirm_delete.html b/catalog/templates/catalog/author_confirm_delete.html
index 6d4b6417..0c4a4553 100644
--- a/catalog/templates/catalog/author_confirm_delete.html
+++ b/catalog/templates/catalog/author_confirm_delete.html
@@ -2,9 +2,9 @@
{% block content %}
-Delete Author
+Delete Author: {{ author }}
-Are you sure you want to delete the author: {{ author }}?
+Are you sure you want to delete the author?
+
+{% endblock %}
diff --git a/catalog/templates/catalog/bookinstance_detail.html b/catalog/templates/catalog/bookinstance_detail.html
new file mode 100644
index 00000000..a4429f16
--- /dev/null
+++ b/catalog/templates/catalog/bookinstance_detail.html
@@ -0,0 +1,32 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+BookInstance: {{ bookinstance.book.title }}
+
+Author: {{ bookinstance.book.author }}
+
+Imprint: {{ bookinstance.imprint }}
+Status: {{ bookinstance.get_status_display }} {% if bookinstance.status != 'a' %} (Due: {{bookinstance.due_back}}){% endif %}
+
+
+
+{% endblock %}
+
+
+{% block sidebar %}
+ {{ block.super }}
+
+ {% if perms.catalog.can_mark_returned %}
+
+
+ {% endif %}
+
+{% endblock %}
diff --git a/catalog/templates/catalog/bookinstance_form.html b/catalog/templates/catalog/bookinstance_form.html
new file mode 100644
index 00000000..8bae8bca
--- /dev/null
+++ b/catalog/templates/catalog/bookinstance_form.html
@@ -0,0 +1,13 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
+{% endblock %}
diff --git a/catalog/templates/catalog/bookinstance_list.html b/catalog/templates/catalog/bookinstance_list.html
new file mode 100644
index 00000000..0a650424
--- /dev/null
+++ b/catalog/templates/catalog/bookinstance_list.html
@@ -0,0 +1,16 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+ Book Copies in Library
+
+
+ {% for bookinst in bookinstance_list %}
+
+ {{bookinst.book.title}} ({{ bookinst.due_back }}) {% if user.is_staff %}- {{ bookinst.borrower }}{% endif %} {% if perms.catalog.can_mark_returned %}- Renew {% endif %}
+
+ {% empty %}
+ There are no book copies available.
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/catalog/templates/catalog/genre_confirm_delete.html b/catalog/templates/catalog/genre_confirm_delete.html
new file mode 100644
index 00000000..7fca32e6
--- /dev/null
+++ b/catalog/templates/catalog/genre_confirm_delete.html
@@ -0,0 +1,14 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+Delete Genre: {{ genre }}
+
+Are you sure you want to delete the genre?
+
+
+
+{% endblock %}
diff --git a/catalog/templates/catalog/genre_detail.html b/catalog/templates/catalog/genre_detail.html
new file mode 100644
index 00000000..29269bd5
--- /dev/null
+++ b/catalog/templates/catalog/genre_detail.html
@@ -0,0 +1,41 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+Genre: {{ genre.name }}
+
+
+
Books in genre
+
+
+ {% for copy in genre.book_set.all %}
+
+ {{ copy.title }} ({{copy.author}})
+
+ {% empty %}
+ There are no books in this genre.
+ {% endfor %}
+
+
+{% endblock %}
+
+
+{% block sidebar %}
+ {{ block.super }}
+
+ {% if perms.catalog.can_mark_returned %}
+
+
+ {% endif %}
+
+{% endblock %}
+
+
+
+
+
diff --git a/catalog/templates/catalog/genre_form.html b/catalog/templates/catalog/genre_form.html
new file mode 100644
index 00000000..5c01e2ad
--- /dev/null
+++ b/catalog/templates/catalog/genre_form.html
@@ -0,0 +1,13 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
+{% endblock %}
diff --git a/catalog/templates/catalog/genre_list.html b/catalog/templates/catalog/genre_list.html
new file mode 100644
index 00000000..613fb95e
--- /dev/null
+++ b/catalog/templates/catalog/genre_list.html
@@ -0,0 +1,25 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
Genre List
+
+{% if genre_list %}
+
+{% else %}
+
There are no genres available.
+{% endif %}
+
+
+{% endblock %}
+
diff --git a/catalog/templates/catalog/language_confirm_delete.html b/catalog/templates/catalog/language_confirm_delete.html
new file mode 100644
index 00000000..a27ed91b
--- /dev/null
+++ b/catalog/templates/catalog/language_confirm_delete.html
@@ -0,0 +1,14 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
Delete Language: {{ language }}
+
+
Are you sure you want to delete the language?
+
+
+
+{% endblock %}
diff --git a/catalog/templates/catalog/language_detail.html b/catalog/templates/catalog/language_detail.html
new file mode 100644
index 00000000..20ece7f5
--- /dev/null
+++ b/catalog/templates/catalog/language_detail.html
@@ -0,0 +1,41 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
Language: {{ language.name }}
+
+
+
Books in language
+
+
+ {% for copy in language.book_set.all %}
+
+ {{ copy.title }}
+
+ {% empty %}
+ There are no books in this language.
+ {% endfor %}
+
+
+{% endblock %}
+
+
+{% block sidebar %}
+ {{ block.super }}
+
+ {% if perms.catalog.can_mark_returned %}
+
+
+ {% endif %}
+
+{% endblock %}
+
+
+
+
+
diff --git a/catalog/templates/catalog/language_form.html b/catalog/templates/catalog/language_form.html
new file mode 100644
index 00000000..5c01e2ad
--- /dev/null
+++ b/catalog/templates/catalog/language_form.html
@@ -0,0 +1,13 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
+{% endblock %}
diff --git a/catalog/templates/catalog/language_list.html b/catalog/templates/catalog/language_list.html
new file mode 100644
index 00000000..747a7a49
--- /dev/null
+++ b/catalog/templates/catalog/language_list.html
@@ -0,0 +1,18 @@
+{% extends "base_generic.html" %}
+
+{% block content %}
+
+
Language List
+
+
+ {% for language in language_list %}
+
+ {{ language }}
+
+ {% empty %}
+ There are no languages available.
+ {% endfor %}
+
+
+{% endblock %}
+
diff --git a/catalog/urls.py b/catalog/urls.py
index b65bd17d..51e08549 100644
--- a/catalog/urls.py
+++ b/catalog/urls.py
@@ -38,3 +38,32 @@
path('book/
/update/', views.BookUpdate.as_view(), name='book-update'),
path('book//delete/', views.BookDelete.as_view(), name='book-delete'),
]
+
+
+
+# Add URLConf to list, view, create, update, and delete genre
+urlpatterns += [
+ path('genres/', views.GenreListView.as_view(), name='genres'),
+ path('genre/', views.GenreDetailView.as_view(), name='genre-detail'),
+ path('genre/create/', views.GenreCreate.as_view(), name='genre-create'),
+ path('genre//update/', views.GenreUpdate.as_view(), name='genre-update'),
+ path('genre//delete/', views.GenreDelete.as_view(), name='genre-delete'),
+]
+
+# Add URLConf to list, view, create, update, and delete languages
+urlpatterns += [
+ path('languages/', views.LanguageListView.as_view(), name='languages'),
+ path('language/', views.LanguageDetailView.as_view(), name='language-detail'),
+ path('language/create/', views.LanguageCreate.as_view(), name='language-create'),
+ path('language//update/', views.LanguageUpdate.as_view(), name='language-update'),
+ path('language//delete/', views.LanguageDelete.as_view(), name='language-delete'),
+]
+
+# Add URLConf to list, view, create, update, and delete bookinstances
+urlpatterns += [
+ path('bookinstances/', views.BookInstanceListView.as_view(), name='bookinstances'),
+ path('bookinstance/', views.BookInstanceDetailView.as_view(), name='bookinstance-detail'),
+ path('bookinstance/create/', views.BookInstanceCreate.as_view(), name='bookinstance-create'),
+ path('bookinstance//update/', views.BookInstanceUpdate.as_view(), name='bookinstance-update'),
+ path('bookinstance//delete/', views.BookInstanceDelete.as_view(), name='bookinstance-delete'),
+]
diff --git a/catalog/views.py b/catalog/views.py
index 1cdf7ba0..3f46cb93 100644
--- a/catalog/views.py
+++ b/catalog/views.py
@@ -1,8 +1,20 @@
+from .models import Author
+from django.urls import reverse_lazy
+from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from catalog.forms import RenewBookForm
+from django.contrib.auth.decorators import login_required, permission_required
+import datetime
+from django.urls import reverse
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.contrib.auth.mixins import PermissionRequiredMixin
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.views import generic
from django.shortcuts import render
# Create your views here.
-from .models import Book, Author, BookInstance, Genre
+from .models import Book, Author, BookInstance, Genre, Language
def index(request):
@@ -11,7 +23,8 @@ def index(request):
num_books = Book.objects.all().count()
num_instances = BookInstance.objects.all().count()
# Available copies of books
- num_instances_available = BookInstance.objects.filter(status__exact='a').count()
+ num_instances_available = BookInstance.objects.filter(
+ status__exact='a').count()
num_authors = Author.objects.count() # The 'all()' is implied by default.
# Number of visits to this view, as counted in the session variable.
@@ -28,9 +41,6 @@ def index(request):
)
-from django.views import generic
-
-
class BookListView(generic.ListView):
"""Generic class-based view for a list of books."""
model = Book
@@ -53,7 +63,37 @@ class AuthorDetailView(generic.DetailView):
model = Author
-from django.contrib.auth.mixins import LoginRequiredMixin
+class GenreDetailView(generic.DetailView):
+ """Generic class-based detail view for a genre."""
+ model = Genre
+
+
+class GenreListView(generic.ListView):
+ """Generic class-based list view for a list of genres."""
+ model = Genre
+ paginate_by = 10
+
+
+class LanguageDetailView(generic.DetailView):
+ """Generic class-based detail view for a genre."""
+ model = Language
+
+
+class LanguageListView(generic.ListView):
+ """Generic class-based list view for a list of genres."""
+ model = Language
+ paginate_by = 10
+
+
+class BookInstanceListView(generic.ListView):
+ """Generic class-based view for a list of books."""
+ model = BookInstance
+ paginate_by = 10
+
+
+class BookInstanceDetailView(generic.DetailView):
+ """Generic class-based detail view for a book."""
+ model = BookInstance
class LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView):
@@ -71,7 +111,6 @@ def get_queryset(self):
# Added as part of challenge!
-from django.contrib.auth.mixins import PermissionRequiredMixin
class LoanedBooksAllListView(PermissionRequiredMixin, generic.ListView):
@@ -85,14 +124,7 @@ def get_queryset(self):
return BookInstance.objects.filter(status__exact='o').order_by('due_back')
-from django.shortcuts import get_object_or_404
-from django.http import HttpResponseRedirect
-from django.urls import reverse
-import datetime
-from django.contrib.auth.decorators import login_required, permission_required
-
# from .forms import RenewBookForm
-from catalog.forms import RenewBookForm
@login_required
@@ -129,11 +161,6 @@ def renew_book_librarian(request, pk):
return render(request, 'catalog/book_renew_librarian.html', context)
-from django.views.generic.edit import CreateView, UpdateView, DeleteView
-from django.urls import reverse_lazy
-from .models import Author
-
-
class AuthorCreate(PermissionRequiredMixin, CreateView):
model = Author
fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']
@@ -143,7 +170,8 @@ class AuthorCreate(PermissionRequiredMixin, CreateView):
class AuthorUpdate(PermissionRequiredMixin, UpdateView):
model = Author
- fields = '__all__' # Not recommended (potential security issue if more fields added)
+ # Not recommended (potential security issue if more fields added)
+ fields = '__all__'
permission_required = 'catalog.can_mark_returned'
@@ -170,3 +198,58 @@ class BookDelete(PermissionRequiredMixin, DeleteView):
model = Book
success_url = reverse_lazy('books')
permission_required = 'catalog.can_mark_returned'
+
+
+class GenreCreate(PermissionRequiredMixin, CreateView):
+ model = Genre
+ fields = ['name', ]
+ permission_required = 'catalog.can_mark_returned'
+
+
+class GenreUpdate(PermissionRequiredMixin, UpdateView):
+ model = Genre
+ fields = ['name', ]
+ permission_required = 'catalog.can_mark_returned'
+
+
+class GenreDelete(PermissionRequiredMixin, DeleteView):
+ model = Genre
+ success_url = reverse_lazy('genres')
+ permission_required = 'catalog.can_mark_returned'
+
+
+class LanguageCreate(PermissionRequiredMixin, CreateView):
+ model = Language
+ fields = ['name', ]
+ permission_required = 'catalog.can_mark_returned'
+
+
+class LanguageUpdate(PermissionRequiredMixin, UpdateView):
+ model = Language
+ fields = ['name', ]
+ permission_required = 'catalog.can_mark_returned'
+
+
+class LanguageDelete(PermissionRequiredMixin, DeleteView):
+ model = Language
+ success_url = reverse_lazy('languages')
+ permission_required = 'catalog.can_mark_returned'
+
+
+class BookInstanceCreate(PermissionRequiredMixin, CreateView):
+ model = BookInstance
+ fields = ['book','imprint', 'due_back', 'borrower', 'status']
+ permission_required = 'catalog.can_mark_returned'
+
+
+class BookInstanceUpdate(PermissionRequiredMixin, UpdateView):
+ model = BookInstance
+ # fields = "__all__"
+ fields = ['imprint', 'due_back', 'borrower', 'status']
+ permission_required = 'catalog.can_mark_returned'
+
+
+class BookInstanceDelete(PermissionRequiredMixin, DeleteView):
+ model = BookInstance
+ success_url = reverse_lazy('bookinstances')
+ permission_required = 'catalog.can_mark_returned'