diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 4c3e7ee..059f4c9 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 2 matrix: - python-version: ['3.12', '3.13'] + python-version: ['3.13', '3.14'] steps: - uses: actions/checkout@v3 diff --git a/ram/bookshelf/admin.py b/ram/bookshelf/admin.py index ba57d98..4fea47f 100644 --- a/ram/bookshelf/admin.py +++ b/ram/bookshelf/admin.py @@ -2,7 +2,7 @@ import html from django.conf import settings from django.contrib import admin -from django.utils.html import format_html, strip_tags +from django.utils.html import format_html, format_html_join, strip_tags from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin from ram.admin import publish, unpublish @@ -133,13 +133,14 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin): @admin.display(description="Invoices") def invoices(self, obj): if obj.invoice.exists(): - html = "
".join( - "{}".format( - i.file.url, i - ) for i in obj.invoice.all()) + html = format_html_join( + "
", + "{}", + ((i.file.url, i) for i in obj.invoice.all()) + ) else: html = "-" - return format_html(html) + return html @admin.display(description="Publisher") def get_publisher(self, obj): @@ -217,7 +218,7 @@ class PublisherAdmin(admin.ModelAdmin): @admin.display(description="Country") def country_flag(self, obj): return format_html( - ' {}'.format(obj.country.flag, obj.country.name) + ' {}', obj.country.flag, obj.country.name ) @@ -295,13 +296,14 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin): @admin.display(description="Invoices") def invoices(self, obj): if obj.invoice.exists(): - html = "
".join( - "{}".format( - i.file.url, i - ) for i in obj.invoice.all()) + html = format_html_join( + "
", + "{}", + ((i.file.url, i) for i in obj.invoice.all()) + ) else: html = "-" - return format_html(html) + return html def download_csv(modeladmin, request, queryset): header = [ diff --git a/ram/bookshelf/migrations/0016_basebook_book_catalogue.py b/ram/bookshelf/migrations/0016_basebook_book_catalogue.py index 5e44540..e465782 100644 --- a/ram/bookshelf/migrations/0016_basebook_book_catalogue.py +++ b/ram/bookshelf/migrations/0016_basebook_book_catalogue.py @@ -1,7 +1,8 @@ # Generated by Django 5.1.2 on 2024-11-27 16:35 import django.db.models.deletion -from django.db import migrations, models +from django.db import migrations, models, connection +from django.db.utils import ProgrammingError, OperationalError def basebook_to_book(apps, schema_editor): @@ -16,6 +17,19 @@ def basebook_to_book(apps, schema_editor): b.authors.set(row.old_authors.all()) +def drop_temporary_tables(apps, schema_editor): + try: + with connection.cursor() as cursor: + cursor.execute( + 'DROP TABLE IF EXISTS bookshelf_basebook_old_authors' + ) + cursor.execute( + 'DROP TABLE IF EXISTS bookshelf_basebook_authors' + ) + except (ProgrammingError, OperationalError): + pass + + class Migration(migrations.Migration): dependencies = [ @@ -101,10 +115,6 @@ class Migration(migrations.Migration): model_name="basebook", name="old_title", ), - migrations.RemoveField( - model_name="basebook", - name="old_authors", - ), migrations.RemoveField( model_name="basebook", name="old_publisher", @@ -138,4 +148,16 @@ class Migration(migrations.Migration): }, bases=("bookshelf.basebook",), ), + # Required by Dajngo 6.0 on SQLite + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.RemoveField( + model_name="basebook", + name="old_authors", + ), + ], + database_operations=[ + migrations.RunPython(drop_temporary_tables) + ] + ), ] diff --git a/ram/bookshelf/migrations/0024_alter_basebook_language.py b/ram/bookshelf/migrations/0024_alter_basebook_language.py new file mode 100644 index 0000000..b7f5c5c --- /dev/null +++ b/ram/bookshelf/migrations/0024_alter_basebook_language.py @@ -0,0 +1,123 @@ +# Generated by Django 6.0 on 2025-12-03 22:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookshelf", "0023_delete_basebookdocument"), + ] + + operations = [ + migrations.AlterField( + model_name="basebook", + name="language", + field=models.CharField( + choices=[ + ("af", "Afrikaans"), + ("ar", "Arabic"), + ("ar-dz", "Algerian Arabic"), + ("ast", "Asturian"), + ("az", "Azerbaijani"), + ("bg", "Bulgarian"), + ("be", "Belarusian"), + ("bn", "Bengali"), + ("br", "Breton"), + ("bs", "Bosnian"), + ("ca", "Catalan"), + ("ckb", "Central Kurdish (Sorani)"), + ("cs", "Czech"), + ("cy", "Welsh"), + ("da", "Danish"), + ("de", "German"), + ("dsb", "Lower Sorbian"), + ("el", "Greek"), + ("en", "English"), + ("en-au", "Australian English"), + ("en-gb", "British English"), + ("eo", "Esperanto"), + ("es", "Spanish"), + ("es-ar", "Argentinian Spanish"), + ("es-co", "Colombian Spanish"), + ("es-mx", "Mexican Spanish"), + ("es-ni", "Nicaraguan Spanish"), + ("es-ve", "Venezuelan Spanish"), + ("et", "Estonian"), + ("eu", "Basque"), + ("fa", "Persian"), + ("fi", "Finnish"), + ("fr", "French"), + ("fy", "Frisian"), + ("ga", "Irish"), + ("gd", "Scottish Gaelic"), + ("gl", "Galician"), + ("he", "Hebrew"), + ("hi", "Hindi"), + ("hr", "Croatian"), + ("hsb", "Upper Sorbian"), + ("ht", "Haitian Creole"), + ("hu", "Hungarian"), + ("hy", "Armenian"), + ("ia", "Interlingua"), + ("id", "Indonesian"), + ("ig", "Igbo"), + ("io", "Ido"), + ("is", "Icelandic"), + ("it", "Italian"), + ("ja", "Japanese"), + ("ka", "Georgian"), + ("kab", "Kabyle"), + ("kk", "Kazakh"), + ("km", "Khmer"), + ("kn", "Kannada"), + ("ko", "Korean"), + ("ky", "Kyrgyz"), + ("lb", "Luxembourgish"), + ("lt", "Lithuanian"), + ("lv", "Latvian"), + ("mk", "Macedonian"), + ("ml", "Malayalam"), + ("mn", "Mongolian"), + ("mr", "Marathi"), + ("ms", "Malay"), + ("my", "Burmese"), + ("nb", "Norwegian Bokmål"), + ("ne", "Nepali"), + ("nl", "Dutch"), + ("nn", "Norwegian Nynorsk"), + ("os", "Ossetic"), + ("pa", "Punjabi"), + ("pl", "Polish"), + ("pt", "Portuguese"), + ("pt-br", "Brazilian Portuguese"), + ("ro", "Romanian"), + ("ru", "Russian"), + ("sk", "Slovak"), + ("sl", "Slovenian"), + ("sq", "Albanian"), + ("sr", "Serbian"), + ("sr-latn", "Serbian Latin"), + ("sv", "Swedish"), + ("sw", "Swahili"), + ("ta", "Tamil"), + ("te", "Telugu"), + ("tg", "Tajik"), + ("th", "Thai"), + ("tk", "Turkmen"), + ("tr", "Turkish"), + ("tt", "Tatar"), + ("udm", "Udmurt"), + ("ug", "Uyghur"), + ("uk", "Ukrainian"), + ("ur", "Urdu"), + ("uz", "Uzbek"), + ("vi", "Vietnamese"), + ("zh-hans", "Simplified Chinese"), + ("zh-hant", "Traditional Chinese"), + ], + default="en", + max_length=7, + ), + ), + ] diff --git a/ram/consist/admin.py b/ram/consist/admin.py index 472b6a0..135760a 100644 --- a/ram/consist/admin.py +++ b/ram/consist/admin.py @@ -54,7 +54,7 @@ class ConsistAdmin(SortableAdminBase, admin.ModelAdmin): @admin.display(description="Country") def country_flag(self, obj): return format_html( - ' {}'.format(obj.country.flag, obj.country) + ' {}', obj.country.flag, obj.country ) fieldsets = ( diff --git a/ram/metadata/admin.py b/ram/metadata/admin.py index c017394..d3f30b5 100644 --- a/ram/metadata/admin.py +++ b/ram/metadata/admin.py @@ -54,7 +54,7 @@ class CompanyAdmin(admin.ModelAdmin): @admin.display(description="Country") def country_flag(self, obj): return format_html( - ' {}'.format(obj.country.flag, obj.country.name) + ' {}', obj.country.flag, obj.country.name ) @@ -68,7 +68,7 @@ class ManufacturerAdmin(admin.ModelAdmin): @admin.display(description="Country") def country_flag(self, obj): return format_html( - ' {}'.format(obj.country.flag, obj.country.name) + ' {}', obj.country.flag, obj.country.name ) diff --git a/ram/portal/templates/includes/login.html b/ram/portal/templates/includes/login.html index f9d69a7..6001cbc 100644 --- a/ram/portal/templates/includes/login.html +++ b/ram/portal/templates/includes/login.html @@ -11,9 +11,10 @@