mirror of
https://github.com/daniviga/django-ram.git
synced 2025-12-26 15:28:31 +01:00
Stabilize the magazine repository app
This commit is contained in:
@@ -76,7 +76,7 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
autocomplete_fields = ("authors", "publisher", "shop")
|
autocomplete_fields = ("authors", "publisher", "shop")
|
||||||
readonly_fields = ("invoices", "creation_time", "updated_time")
|
readonly_fields = ("invoices", "creation_time", "updated_time")
|
||||||
search_fields = ("title", "publisher__name", "authors__last_name")
|
search_fields = ("title", "publisher__name", "authors__last_name")
|
||||||
list_filter = ("publisher__name", "authors")
|
list_filter = ("publisher__name", "authors", "published")
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
@@ -239,7 +239,12 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
autocomplete_fields = ("manufacturer",)
|
autocomplete_fields = ("manufacturer",)
|
||||||
readonly_fields = ("invoices", "creation_time", "updated_time")
|
readonly_fields = ("invoices", "creation_time", "updated_time")
|
||||||
search_fields = ("manufacturer__name", "years", "scales__scale")
|
search_fields = ("manufacturer__name", "years", "scales__scale")
|
||||||
list_filter = ("manufacturer__name", "publication_year", "scales__scale")
|
list_filter = (
|
||||||
|
"manufacturer__name",
|
||||||
|
"publication_year",
|
||||||
|
"scales__scale",
|
||||||
|
"published",
|
||||||
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
@@ -358,8 +363,8 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
actions = [publish, unpublish, download_csv]
|
actions = [publish, unpublish, download_csv]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Issue)
|
@admin.register(MagazineIssue)
|
||||||
class MagazineIssueAdmin(admin.ModelAdmin):
|
class MagazineIssueAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||||
inlines = (
|
inlines = (
|
||||||
BookPropertyInline,
|
BookPropertyInline,
|
||||||
BookImageInline,
|
BookImageInline,
|
||||||
@@ -370,10 +375,8 @@ class MagazineIssueAdmin(admin.ModelAdmin):
|
|||||||
"issue_number",
|
"issue_number",
|
||||||
"published",
|
"published",
|
||||||
)
|
)
|
||||||
# autocomplete_fields = ("publisher",)
|
autocomplete_fields = ("shop",)
|
||||||
# readonly_fields = ("creation_time", "updated_time")
|
readonly_fields = ("magazine", "creation_time", "updated_time")
|
||||||
# search_fields = ("title", "publisher__name")
|
|
||||||
# list_filter = ("publisher__name", "language")
|
|
||||||
|
|
||||||
def get_model_perms(self, request):
|
def get_model_perms(self, request):
|
||||||
"""
|
"""
|
||||||
@@ -381,14 +384,106 @@ class MagazineIssueAdmin(admin.ModelAdmin):
|
|||||||
"""
|
"""
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"published",
|
||||||
|
"magazine",
|
||||||
|
"issue_number",
|
||||||
|
"publication_year",
|
||||||
|
"publication_month",
|
||||||
|
"ISBN",
|
||||||
|
"language",
|
||||||
|
"number_of_pages",
|
||||||
|
"description",
|
||||||
|
"tags",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Purchase data",
|
||||||
|
{
|
||||||
|
"classes": ("collapse",),
|
||||||
|
"fields": (
|
||||||
|
"shop",
|
||||||
|
"purchase_date",
|
||||||
|
"price",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Notes",
|
||||||
|
{"classes": ("collapse",), "fields": ("notes",)},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Audit",
|
||||||
|
{
|
||||||
|
"classes": ("collapse",),
|
||||||
|
"fields": (
|
||||||
|
"creation_time",
|
||||||
|
"updated_time",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
actions = [publish, unpublish]
|
actions = [publish, unpublish]
|
||||||
|
|
||||||
|
|
||||||
|
class MagazineIssueInline(admin.StackedInline):
|
||||||
|
model = MagazineIssue
|
||||||
|
min_num = 0
|
||||||
|
extra = 0
|
||||||
|
autocomplete_fields = ("shop",)
|
||||||
|
show_change_link = True
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"published",
|
||||||
|
"issue_number",
|
||||||
|
"publication_year",
|
||||||
|
"publication_month",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Additional info",
|
||||||
|
{
|
||||||
|
"classes": ("collapse",),
|
||||||
|
"fields": (
|
||||||
|
"language",
|
||||||
|
"number_of_pages",
|
||||||
|
"ISBN",
|
||||||
|
"tags",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Purchase data",
|
||||||
|
{
|
||||||
|
"classes": ("collapse",),
|
||||||
|
"fields": (
|
||||||
|
"shop",
|
||||||
|
"purchase_date",
|
||||||
|
"price",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Media:
|
||||||
|
js = ('admin/js/magazine_issue_defaults.js',)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Magazine)
|
@admin.register(Magazine)
|
||||||
class MagazineAdmin(admin.ModelAdmin):
|
class MagazineAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||||
inlines = (
|
inlines = (
|
||||||
MagazineIssueInline,
|
MagazineIssueInline,
|
||||||
)
|
)
|
||||||
|
|
||||||
list_display = (
|
list_display = (
|
||||||
"__str__",
|
"__str__",
|
||||||
"publisher",
|
"publisher",
|
||||||
@@ -397,6 +492,37 @@ class MagazineAdmin(admin.ModelAdmin):
|
|||||||
autocomplete_fields = ("publisher",)
|
autocomplete_fields = ("publisher",)
|
||||||
readonly_fields = ("creation_time", "updated_time")
|
readonly_fields = ("creation_time", "updated_time")
|
||||||
search_fields = ("name", "publisher__name")
|
search_fields = ("name", "publisher__name")
|
||||||
list_filter = ("publisher__name", "language")
|
list_filter = ("publisher__name", "published")
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
{
|
||||||
|
"fields": (
|
||||||
|
"published",
|
||||||
|
"name",
|
||||||
|
"publisher",
|
||||||
|
"ISBN",
|
||||||
|
"language",
|
||||||
|
"description",
|
||||||
|
"image",
|
||||||
|
"tags",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Notes",
|
||||||
|
{"classes": ("collapse",), "fields": ("notes",)},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Audit",
|
||||||
|
{
|
||||||
|
"classes": ("collapse",),
|
||||||
|
"fields": (
|
||||||
|
"creation_time",
|
||||||
|
"updated_time",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
actions = [publish, unpublish]
|
actions = [publish, unpublish]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.2.8 on 2025-11-13 23:01
|
# Generated by Django 6.0 on 2025-12-08 17:47
|
||||||
|
|
||||||
import bookshelf.models
|
import bookshelf.models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@@ -11,32 +11,11 @@ from django.db import migrations, models
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("bookshelf", "0023_delete_basebookdocument"),
|
("bookshelf", "0024_alter_basebook_language"),
|
||||||
("metadata", "0025_alter_company_options_alter_manufacturer_options_and_more"),
|
("metadata", "0025_alter_company_options_alter_manufacturer_options_and_more"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
|
||||||
name="Issue",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"basebook_ptr",
|
|
||||||
models.OneToOneField(
|
|
||||||
auto_created=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
parent_link=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
to="bookshelf.basebook",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("issue_number", models.CharField(max_length=100)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"abstract": False,
|
|
||||||
},
|
|
||||||
bases=("bookshelf.basebook",),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Magazine",
|
name="Magazine",
|
||||||
fields=[
|
fields=[
|
||||||
@@ -55,12 +34,13 @@ class Migration(migrations.Migration):
|
|||||||
("updated_time", models.DateTimeField(auto_now=True)),
|
("updated_time", models.DateTimeField(auto_now=True)),
|
||||||
("published", models.BooleanField(default=True)),
|
("published", models.BooleanField(default=True)),
|
||||||
("name", models.CharField(max_length=200)),
|
("name", models.CharField(max_length=200)),
|
||||||
|
("ISBN", models.CharField(blank=True, max_length=17)),
|
||||||
(
|
(
|
||||||
"image",
|
"image",
|
||||||
models.ImageField(
|
models.ImageField(
|
||||||
blank=True,
|
blank=True,
|
||||||
storage=ram.utils.DeduplicatedStorage,
|
storage=ram.utils.DeduplicatedStorage,
|
||||||
upload_to=bookshelf.models.magazine_image_upload,
|
upload_to=bookshelf.models.book_image_upload,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -108,6 +88,7 @@ class Migration(migrations.Migration):
|
|||||||
("hi", "Hindi"),
|
("hi", "Hindi"),
|
||||||
("hr", "Croatian"),
|
("hr", "Croatian"),
|
||||||
("hsb", "Upper Sorbian"),
|
("hsb", "Upper Sorbian"),
|
||||||
|
("ht", "Haitian Creole"),
|
||||||
("hu", "Hungarian"),
|
("hu", "Hungarian"),
|
||||||
("hy", "Armenian"),
|
("hy", "Armenian"),
|
||||||
("ia", "Interlingua"),
|
("ia", "Interlingua"),
|
||||||
@@ -193,34 +174,51 @@ class Migration(migrations.Migration):
|
|||||||
name="MagazineIssue",
|
name="MagazineIssue",
|
||||||
fields=[
|
fields=[
|
||||||
(
|
(
|
||||||
"id",
|
"basebook_ptr",
|
||||||
models.BigAutoField(
|
models.OneToOneField(
|
||||||
auto_created=True,
|
auto_created=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True,
|
||||||
primary_key=True,
|
primary_key=True,
|
||||||
serialize=False,
|
serialize=False,
|
||||||
verbose_name="ID",
|
to="bookshelf.basebook",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
("issue_number", models.CharField(max_length=100)),
|
||||||
(
|
(
|
||||||
"issue",
|
"publication_month",
|
||||||
models.ForeignKey(
|
models.SmallIntegerField(
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
blank=True,
|
||||||
related_name="magazine_issue",
|
choices=[
|
||||||
to="bookshelf.issue",
|
(1, "January"),
|
||||||
|
(2, "February"),
|
||||||
|
(3, "March"),
|
||||||
|
(4, "April"),
|
||||||
|
(5, "May"),
|
||||||
|
(6, "June"),
|
||||||
|
(7, "July"),
|
||||||
|
(8, "August"),
|
||||||
|
(9, "September"),
|
||||||
|
(10, "October"),
|
||||||
|
(11, "November"),
|
||||||
|
(12, "December"),
|
||||||
|
],
|
||||||
|
null=True,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"magazine",
|
"magazine",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
related_name="magazine_issue",
|
related_name="issue",
|
||||||
to="bookshelf.magazine",
|
to="bookshelf.magazine",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"ordering": ["magazine", "issue"],
|
"ordering": ["magazine", "issue_number"],
|
||||||
"unique_together": {("magazine", "issue")},
|
"unique_together": {("magazine", "issue_number")},
|
||||||
},
|
},
|
||||||
|
bases=("bookshelf.basebook",),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -4,6 +4,7 @@ from django.db import models
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.dates import MONTHS
|
from django.utils.dates import MONTHS
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django_countries.fields import CountryField
|
from django_countries.fields import CountryField
|
||||||
|
|
||||||
from ram.utils import DeduplicatedStorage
|
from ram.utils import DeduplicatedStorage
|
||||||
@@ -212,4 +213,11 @@ class MagazineIssue(BaseBook):
|
|||||||
ordering = ["magazine", "issue_number"]
|
ordering = ["magazine", "issue_number"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.magazine.name} - {self.issue.issue_number}"
|
return f"{self.magazine.name} - {self.issue_number}"
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if self.magazine.published is False and self.published is True:
|
||||||
|
raise ValidationError(
|
||||||
|
"Cannot set an issue as published if the magazine is not "
|
||||||
|
"published."
|
||||||
|
)
|
||||||
|
|||||||
16
ram/bookshelf/static/admin/js/magazine_issue_defaults.js
Normal file
16
ram/bookshelf/static/admin/js/magazine_issue_defaults.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
document.addEventListener('formset:added', function(event) {
|
||||||
|
const newForm = event.target; // the new inline form element
|
||||||
|
|
||||||
|
const defaultLanguage = document.querySelector('#id_language').value;
|
||||||
|
const defaultStatus = document.querySelector('#id_published').checked;
|
||||||
|
|
||||||
|
const languageInput = newForm.querySelector('select[name$="language"]');
|
||||||
|
const statusInput = newForm.querySelector('input[name$="published"]');
|
||||||
|
|
||||||
|
if (languageInput) {
|
||||||
|
languageInput.value = defaultLanguage;
|
||||||
|
}
|
||||||
|
if (statusInput) {
|
||||||
|
statusInput.checked = defaultStatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
65
ram/repository/migrations/0004_magazineissuedocument.py
Normal file
65
ram/repository/migrations/0004_magazineissuedocument.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Generated by Django 6.0 on 2025-12-08 17:47
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import ram.utils
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookshelf", "0025_magazine_magazineissue"),
|
||||||
|
(
|
||||||
|
"repository",
|
||||||
|
"0003_alter_bookdocument_file_alter_catalogdocument_file_and_more",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="MagazineIssueDocument",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("description", models.CharField(blank=True, max_length=128)),
|
||||||
|
(
|
||||||
|
"file",
|
||||||
|
models.FileField(
|
||||||
|
storage=ram.utils.DeduplicatedStorage, upload_to="files/"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("creation_time", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated_time", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"private",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Document will be visible only to logged users",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"issue",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="document",
|
||||||
|
to="bookshelf.magazineissue",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name_plural": "Magazines documents",
|
||||||
|
"constraints": [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=("issue", "file"), name="unique_issue_file"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -5,7 +5,7 @@ from tinymce import models as tinymce
|
|||||||
from ram.models import PrivateDocument
|
from ram.models import PrivateDocument
|
||||||
from metadata.models import Decoder, Shop, Tag
|
from metadata.models import Decoder, Shop, Tag
|
||||||
from roster.models import RollingStock
|
from roster.models import RollingStock
|
||||||
from bookshelf.models import Book, Catalog, Issue
|
from bookshelf.models import Book, Catalog, MagazineIssue
|
||||||
|
|
||||||
|
|
||||||
class GenericDocument(PrivateDocument):
|
class GenericDocument(PrivateDocument):
|
||||||
@@ -78,7 +78,7 @@ class CatalogDocument(PrivateDocument):
|
|||||||
|
|
||||||
class MagazineIssueDocument(PrivateDocument):
|
class MagazineIssueDocument(PrivateDocument):
|
||||||
issue = models.ForeignKey(
|
issue = models.ForeignKey(
|
||||||
Issue, on_delete=models.CASCADE, related_name="document"
|
MagazineIssue, on_delete=models.CASCADE, related_name="document"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
Reference in New Issue
Block a user