mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
Add a book details page in bookshelf
This commit is contained in:
@@ -58,8 +58,8 @@ class Book(models.Model):
|
|||||||
def publisher_name(self):
|
def publisher_name(self):
|
||||||
return self.publisher.name
|
return self.publisher.name
|
||||||
|
|
||||||
# def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
# return reverse("books", kwargs={"uuid": self.uuid})
|
return reverse("book", kwargs={"uuid": self.uuid})
|
||||||
|
|
||||||
|
|
||||||
class BookImage(Image):
|
class BookImage(Image):
|
||||||
|
@@ -171,7 +171,7 @@
|
|||||||
<li><a class="dropdown-item" href="{% url 'manufacturers' category='real' %}">Real</a></li>
|
<li><a class="dropdown-item" href="{% url 'manufacturers' category='real' %}">Real</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% show_flatpage_menu %}
|
{% show_flatpages_menu %}
|
||||||
{% show_bookshelf_menu %}
|
{% show_bookshelf_menu %}
|
||||||
</ul>
|
</ul>
|
||||||
{% include 'includes/search.html' %}
|
{% include 'includes/search.html' %}
|
||||||
|
125
ram/portal/templates/bookshelf/book.html
Normal file
125
ram/portal/templates/bookshelf/book.html
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
{% if book.tags.all %}
|
||||||
|
<p><small>Tags:</small>
|
||||||
|
{% for t in book.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
||||||
|
{{ t.name }}</a>{# new line is required #}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<small class="text-muted">Updated {{ book.updated_time | date:"M d, Y H:i" }}</small>
|
||||||
|
{% endblock %}
|
||||||
|
{% block carousel %}
|
||||||
|
<div class="row">
|
||||||
|
<div id="carouselControls" class="carousel carousel-dark slide" data-bs-ride="carousel" data-bs-interval="10000">
|
||||||
|
<div class="carousel-inner">
|
||||||
|
{% for t in book.image.all %}
|
||||||
|
{% if forloop.first %}
|
||||||
|
<div class="carousel-item active">
|
||||||
|
{% else %}
|
||||||
|
<div class="carousel-item">
|
||||||
|
{% endif %}
|
||||||
|
<img src="{{ t.image.url }}" class="d-block w-100 rounded img-thumbnail" alt="...">
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if book.image.count > 1 %}
|
||||||
|
<button class="carousel-control-prev" type="button" data-bs-target="#carouselControls" data-bs-slide="prev">
|
||||||
|
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Previous</span>
|
||||||
|
</button>
|
||||||
|
<button class="carousel-control-next" type="button" data-bs-target="#carouselControls" data-bs-slide="next">
|
||||||
|
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Next</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block cards %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block extra_content %}
|
||||||
|
<section class="py-4 text-start container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="mx-auto">
|
||||||
|
<nav>
|
||||||
|
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||||
|
<button class="nav-link active" id="nav-summary-tab" data-bs-toggle="tab" data-bs-target="#nav-summary" type="button" role="tab" aria-controls="nav-summary" aria-selected="true">Summary</button>
|
||||||
|
{% if book.notes %}<button class="nav-link" id="nav-notes-tab" data-bs-toggle="tab" data-bs-target="#nav-notes" type="button" role="tab" aria-controls="nav-notes" aria-selected="false">Notes</button>{% endif %}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="tab-content" id="nav-tabContent">
|
||||||
|
<div class="tab-pane fade show active" id="nav-summary" role="tabpanel" aria-labelledby="nav-summary-tab">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2" scope="row">Book</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table-group-divider">
|
||||||
|
<tr>
|
||||||
|
<th class="w-33" scope="row">Title</th>
|
||||||
|
<td>{{ book.title }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Authors</th>
|
||||||
|
<td>
|
||||||
|
<ul class="mb-0 list-unstyled">{% for a in book.authors.all %}<li>{{ a }}</li>{% endfor %}</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Publisher</th>
|
||||||
|
<td>{{ book.publisher }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">ISBN</th>
|
||||||
|
<td>{{ book.ISBN|default:"-" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Language</th>
|
||||||
|
<td>{{ book.get_language_display }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Number of pages</th>
|
||||||
|
<td>{{ book.number_of_pages|default:"-" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Publication year</th>
|
||||||
|
<td>{{ book.publication_year|default:"-" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Purchase date</th>
|
||||||
|
<td>{{ book.purchase_date|default:"-" }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% if book_properties %}
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2" scope="row">Properties</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table-group-divider">
|
||||||
|
{% for p in book_properties %}
|
||||||
|
<tr>
|
||||||
|
<th class="w-33" scope="row">{{ p.property }}</th>
|
||||||
|
<td>{{ p.value }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="nav-notes" role="tabpanel" aria-labelledby="nav-notes-tab">
|
||||||
|
{{ book.notes | safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
|
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:bookshelf_book_change' book.pk %}">Edit</a>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
@@ -5,16 +5,16 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
{% if d.image.all %}
|
{% if d.image.all %}
|
||||||
{# FIXME <a href="{{ d.get_absolute_url }}"> #}
|
<a href="{{ d.get_absolute_url }}">
|
||||||
{% for r in d.image.all %}
|
{% for r in d.image.all %}
|
||||||
{% if forloop.first %}<img src="{{ r.image.url }}" alt="Card image cap">{% endif %}
|
{% if forloop.first %}<img src="{{ r.image.url }}" alt="Card image cap">{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# FIXME </a> #}
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="card-text" style="position: relative;">
|
<p class="card-text" style="position: relative;">
|
||||||
<strong>{{ d }}</strong>
|
<strong>{{ d }}</strong>
|
||||||
{# <a class="stretched-link" href="{{ d.get_absolute_url }}"></a> #}
|
<a class="stretched-link" href="{{ d.get_absolute_url }}"></a>
|
||||||
</p>
|
</p>
|
||||||
{% if d.tags.all %}
|
{% if d.tags.all %}
|
||||||
<p class="card-text"><small>Tags:</small>
|
<p class="card-text"><small>Tags:</small>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="d-grid gap-2 mb-1 d-md-block">
|
<div class="d-grid gap-2 mb-1 d-md-block">
|
||||||
{# FIXME <a class="btn btn-sm btn-outline-primary" href="{{ d.get_absolute_url }}">Show all data</a> #}
|
<a class="btn btn-sm btn-outline-primary" href="{{ d.get_absolute_url }}">Show all data</a>
|
||||||
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:bookshelf_book_change' d.pk %}">Edit</a>{% endif %}
|
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:bookshelf_book_change' d.pk %}">Edit</a>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -1,10 +1,10 @@
|
|||||||
{% if flatpage_menu %}
|
{% if flatpages_menu %}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="flatpageDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="flatpageDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
Articles
|
Articles
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="flatpageDropdownMenuLink">
|
<ul class="dropdown-menu" aria-labelledby="flatpageDropdownMenuLink">
|
||||||
{% for m in flatpage_menu %}
|
{% for m in flatpages_menu %}
|
||||||
<li><a class="dropdown-item" href="{{ m.get_absolute_url }}">{{ m.name }}</a></li>
|
<li><a class="dropdown-item" href="{{ m.get_absolute_url }}">{{ m.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
@@ -5,12 +5,12 @@ from bookshelf.models import Book
|
|||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('bookshelf_menu.html')
|
@register.inclusion_tag('bookshelf/bookshelf_menu.html')
|
||||||
def show_bookshelf_menu():
|
def show_bookshelf_menu():
|
||||||
return {"bookshelf_menu": Book.objects.exists()}
|
return {"bookshelf_menu": Book.objects.exists()}
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('flatpage_menu.html')
|
@register.inclusion_tag('flatpages/flatpages_menu.html')
|
||||||
def show_flatpage_menu():
|
def show_flatpages_menu():
|
||||||
menu = Flatpage.objects.filter(published=True).order_by("name")
|
menu = Flatpage.objects.filter(published=True).order_by("name")
|
||||||
return {"flatpage_menu": menu}
|
return {"flatpages_menu": menu}
|
||||||
|
@@ -13,6 +13,7 @@ from portal.views import (
|
|||||||
Scales,
|
Scales,
|
||||||
Types,
|
Types,
|
||||||
Books,
|
Books,
|
||||||
|
GetBook,
|
||||||
SearchRoster,
|
SearchRoster,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,8 +56,9 @@ urlpatterns = [
|
|||||||
path("scales/<int:page>", Types.as_view(), name="scales_pagination"),
|
path("scales/<int:page>", Types.as_view(), name="scales_pagination"),
|
||||||
path("types", Types.as_view(), name="types"),
|
path("types", Types.as_view(), name="types"),
|
||||||
path("types/<int:page>", Types.as_view(), name="types_pagination"),
|
path("types/<int:page>", Types.as_view(), name="types_pagination"),
|
||||||
path("books", Books.as_view(), name="books"),
|
path("bookshelf/books", Books.as_view(), name="books"),
|
||||||
path("books/<int:page>", Books.as_view(), name="books_pagination"),
|
path("bookshelf/books/<int:page>", Books.as_view(), name="books_pagination"),
|
||||||
|
path("bookshelf/book/<uuid:uuid>", GetBook.as_view(), name="book"),
|
||||||
path(
|
path(
|
||||||
"search",
|
"search",
|
||||||
SearchRoster.as_view(http_method_names=["post"]),
|
SearchRoster.as_view(http_method_names=["post"]),
|
||||||
|
@@ -344,10 +344,33 @@ class Types(GetData):
|
|||||||
class Books(GetData):
|
class Books(GetData):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.title = "Books"
|
self.title = "Books"
|
||||||
self.template = "books.html"
|
self.template = "bookshelf/books.html"
|
||||||
self.data = Book.objects.all()
|
self.data = Book.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class GetBook(View):
|
||||||
|
def get(self, request, uuid):
|
||||||
|
try:
|
||||||
|
book = Book.objects.get(uuid=uuid)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
book_properties = (
|
||||||
|
book.property.all()
|
||||||
|
if request.user.is_authenticated
|
||||||
|
else book.property.filter(property__private=False)
|
||||||
|
)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"bookshelf/book.html",
|
||||||
|
{
|
||||||
|
"title": book,
|
||||||
|
"book_properties": book_properties,
|
||||||
|
"book": book,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GetFlatpage(View):
|
class GetFlatpage(View):
|
||||||
def get(self, request, flatpage):
|
def get(self, request, flatpage):
|
||||||
try:
|
try:
|
||||||
@@ -359,6 +382,6 @@ class GetFlatpage(View):
|
|||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"flatpage.html",
|
"flatpages/flatpage.html",
|
||||||
{"title": flatpage.name, "flatpage": flatpage},
|
{"title": flatpage.name, "flatpage": flatpage},
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user