Extend the bookshelf implementation

This commit is contained in:
2023-10-02 22:19:04 +02:00
parent 968ebeb0b6
commit 3f905877e7
9 changed files with 172 additions and 59 deletions

View File

@@ -1,6 +1,15 @@
from django.contrib import admin from django.contrib import admin
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
from bookshelf.models import BookProperty, Book, Author, Publisher from bookshelf.models import BookProperty, BookImage, Book, Author, Publisher
class BookImageInline(SortableInlineAdminMixin, admin.TabularInline):
model = BookImage
min_num = 0
extra = 0
readonly_fields = ("image_thumbnail",)
classes = ["collapse"]
class BookPropertyInline(admin.TabularInline): class BookPropertyInline(admin.TabularInline):
@@ -10,8 +19,8 @@ class BookPropertyInline(admin.TabularInline):
@admin.register(Book) @admin.register(Book)
class BookAdmin(admin.ModelAdmin): class BookAdmin(SortableAdminBase, admin.ModelAdmin):
inlines = (BookPropertyInline,) inlines = (BookImageInline, BookPropertyInline,)
list_display = ( list_display = (
"title", "title",
"get_authors", "get_authors",
@@ -19,8 +28,8 @@ class BookAdmin(admin.ModelAdmin):
"publication_year", "publication_year",
"numbers_of_pages" "numbers_of_pages"
) )
search_fields = ("title",) search_fields = ("title", "publisher__name", "authors__last_name")
list_filter = ("publisher__name",) list_filter = ("publisher__name", "authors")
@admin.display(description="Publisher") @admin.display(description="Publisher")
def get_publisher(self, obj): def get_publisher(self, obj):

View File

@@ -0,0 +1,51 @@
# Generated by Django 4.2.5 on 2023-10-02 10:36
from django.db import migrations, models
import django.db.models.deletion
import ram.utils
class Migration(migrations.Migration):
dependencies = [
("bookshelf", "0002_book_language_book_numbers_of_pages_and_more"),
]
operations = [
migrations.CreateModel(
name="BookImage",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("order", models.PositiveIntegerField(default=0)),
(
"image",
models.ImageField(
blank=True,
null=True,
storage=ram.utils.DeduplicatedStorage,
upload_to="images/books/",
),
),
(
"book",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="image",
to="bookshelf.book",
),
),
],
options={
"ordering": ["order"],
"abstract": False,
},
),
]

View File

@@ -5,10 +5,9 @@ from django_countries.fields import CountryField
from ckeditor_uploader.fields import RichTextUploadingField from ckeditor_uploader.fields import RichTextUploadingField
from metadata.models import ( from metadata.models import Tag
Property, from ram.utils import DeduplicatedStorage
Tag, from ram.models import Image, PropertyInstance
)
class Publisher(models.Model): class Publisher(models.Model):
@@ -62,7 +61,19 @@ class Book(models.Model):
# return reverse("rolling_stock", kwargs={"uuid": self.uuid}) # return reverse("rolling_stock", kwargs={"uuid": self.uuid})
class BookProperty(models.Model): class BookImage(Image):
book = models.ForeignKey(
Book, on_delete=models.CASCADE, related_name="image"
)
image = models.ImageField(
upload_to="images/books/", # FIXME, find a better way to replace this
storage=DeduplicatedStorage,
null=True,
blank=True
)
class BookProperty(PropertyInstance):
book = models.ForeignKey( book = models.ForeignKey(
Book, Book,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@@ -70,11 +81,3 @@ class BookProperty(models.Model):
blank=False, blank=False,
related_name="property", related_name="property",
) )
property = models.ForeignKey(Property, on_delete=models.CASCADE)
value = models.CharField(max_length=256)
def __str__(self):
return self.property.name
class Meta:
verbose_name_plural = "Properties"

View File

@@ -0,0 +1,26 @@
from rest_framework import serializers
from bookshelf.models import Book, Author, Publisher
from metadata.serializers import TagSerializer
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__"
class PublisherSerializer(serializers.ModelSerializer):
class Meta:
model = Publisher
fields = "__all__"
class BookSerializer(serializers.ModelSerializer):
authors = AuthorSerializer(many=True)
publisher = PublisherSerializer()
tags = TagSerializer(many=True)
class Meta:
model = Book
fields = "__all__"
read_only_fields = ("creation_time", "updated_time")

7
ram/bookshelf/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import path
from bookshelf.views import BookList, BookGet
urlpatterns = [
path("book/list", BookList.as_view()),
path("book/get/<str:uuid>", BookGet.as_view()),
]

View File

@@ -1,3 +1,18 @@
from django.shortcuts import render from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.schemas.openapi import AutoSchema
# Create your views here. from bookshelf.models import Book
from bookshelf.serializers import BookSerializer
class BookList(ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookGet(RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
lookup_field = "uuid"
schema = AutoSchema(operation_id_base="retrieveBookByUUID")

View File

@@ -3,7 +3,7 @@ import os
from django.db import models from django.db import models
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from ram.utils import DeduplicatedStorage from ram.utils import DeduplicatedStorage, get_image_preview
class Document(models.Model): class Document(models.Model):
@@ -28,3 +28,37 @@ class Document(models.Model):
return mark_safe( return mark_safe(
'<a href="{0}" target="_blank">Link</a>'.format(self.file.url) '<a href="{0}" target="_blank">Link</a>'.format(self.file.url)
) )
class Image(models.Model):
order = models.PositiveIntegerField(default=0, blank=False, null=False)
image = models.ImageField(
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
)
def image_thumbnail(self):
return get_image_preview(self.image.url)
image_thumbnail.short_description = "Preview"
def __str__(self):
return "{0}".format(os.path.basename(self.image.name))
class Meta:
abstract = True
ordering = ["order"]
class PropertyInstance(models.Model):
property = models.ForeignKey(
"metadata.Property", # To avoid circular dependencies
on_delete=models.CASCADE
)
value = models.CharField(max_length=256)
def __str__(self):
return self.property.name
class Meta:
abstract = True
verbose_name_plural = "Properties"

View File

@@ -28,6 +28,7 @@ urlpatterns = [
path("api/v1/consist/", include("consist.urls")), path("api/v1/consist/", include("consist.urls")),
path("api/v1/roster/", include("roster.urls")), path("api/v1/roster/", include("roster.urls")),
path("api/v1/dcc/", include("driver.urls")), path("api/v1/dcc/", include("driver.urls")),
path("api/v1/bookshelf/", include("bookshelf.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG: if settings.DEBUG:

View File

@@ -1,4 +1,3 @@
import os
import re import re
from uuid import uuid4 from uuid import uuid4
from django.db import models from django.db import models
@@ -8,10 +7,9 @@ from django.dispatch import receiver
from ckeditor_uploader.fields import RichTextUploadingField from ckeditor_uploader.fields import RichTextUploadingField
from ram.utils import DeduplicatedStorage, get_image_preview from ram.models import Document, Image, PropertyInstance
from ram.models import Document from ram.utils import get_image_preview
from metadata.models import ( from metadata.models import (
Property,
Scale, Scale,
Manufacturer, Manufacturer,
Decoder, Decoder,
@@ -43,7 +41,7 @@ class RollingClass(models.Model):
return "{0} {1}".format(self.company, self.identifier) return "{0} {1}".format(self.company, self.identifier)
class RollingClassProperty(models.Model): class RollingClassProperty(PropertyInstance):
rolling_class = models.ForeignKey( rolling_class = models.ForeignKey(
RollingClass, RollingClass,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@@ -52,14 +50,6 @@ class RollingClassProperty(models.Model):
related_name="property", related_name="property",
verbose_name="Class", verbose_name="Class",
) )
property = models.ForeignKey(Property, on_delete=models.CASCADE)
value = models.CharField(max_length=256)
def __str__(self):
return self.property.name
class Meta:
verbose_name_plural = "Properties"
class RollingStock(models.Model): class RollingStock(models.Model):
@@ -136,28 +126,13 @@ class RollingStockDocument(Document):
unique_together = ("rolling_stock", "file") unique_together = ("rolling_stock", "file")
class RollingStockImage(models.Model): class RollingStockImage(Image):
order = models.PositiveIntegerField(default=0, blank=False, null=False)
rolling_stock = models.ForeignKey( rolling_stock = models.ForeignKey(
RollingStock, on_delete=models.CASCADE, related_name="image" RollingStock, on_delete=models.CASCADE, related_name="image"
) )
image = models.ImageField(
upload_to="images/", storage=DeduplicatedStorage, null=True, blank=True
)
def image_thumbnail(self):
return get_image_preview(self.image.url)
image_thumbnail.short_description = "Preview"
def __str__(self):
return "{0}".format(os.path.basename(self.image.name))
class Meta:
ordering = ["order"]
class RollingStockProperty(models.Model): class RollingStockProperty(PropertyInstance):
rolling_stock = models.ForeignKey( rolling_stock = models.ForeignKey(
RollingStock, RollingStock,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@@ -165,14 +140,6 @@ class RollingStockProperty(models.Model):
null=False, null=False,
blank=False, blank=False,
) )
property = models.ForeignKey(Property, on_delete=models.CASCADE)
value = models.CharField(max_length=256)
def __str__(self):
return self.property.name
class Meta:
verbose_name_plural = "Properties"
class RollingStockJournal(models.Model): class RollingStockJournal(models.Model):