mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
Extend the bookshelf implementation
This commit is contained in:
@@ -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):
|
||||||
|
51
ram/bookshelf/migrations/0003_bookimage.py
Normal file
51
ram/bookshelf/migrations/0003_bookimage.py
Normal 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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@@ -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"
|
|
||||||
|
26
ram/bookshelf/serializers.py
Normal file
26
ram/bookshelf/serializers.py
Normal 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
7
ram/bookshelf/urls.py
Normal 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()),
|
||||||
|
]
|
@@ -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")
|
||||||
|
@@ -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"
|
||||||
|
@@ -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:
|
||||||
|
@@ -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):
|
||||||
|
Reference in New Issue
Block a user