From 5fcdfbc9c6acd5b06d3d9fc9a58f2334030afe1e Mon Sep 17 00:00:00 2001 From: Bart Schuurmans Date: Sun, 4 Feb 2024 22:36:31 +0100 Subject: [PATCH] WIP: Add Series model --- bookwyrm/forms/books.py | 6 +- bookwyrm/migrations/0206_series_model.py | 148 +++++++++++++++++++++++ bookwyrm/models/__init__.py | 3 + bookwyrm/models/book.py | 7 +- bookwyrm/models/series.py | 35 ++++++ bookwyrm/templates/book/book.html | 23 ++-- 6 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 bookwyrm/migrations/0206_series_model.py create mode 100644 bookwyrm/models/series.py diff --git a/bookwyrm/forms/books.py b/bookwyrm/forms/books.py index f73ce3f5a..e66c67695 100644 --- a/bookwyrm/forms/books.py +++ b/bookwyrm/forms/books.py @@ -1,4 +1,5 @@ """ using django model forms """ + from django import forms from file_resubmit.widgets import ResubmitImageWidget @@ -25,7 +26,6 @@ class EditionForm(CustomForm): "subtitle", "description", "series", - "series_number", "languages", "subjects", "publishers", @@ -55,9 +55,6 @@ class EditionForm(CustomForm): attrs={"aria-describedby": "desc_description"} ), "series": forms.TextInput(attrs={"aria-describedby": "desc_series"}), - "series_number": forms.TextInput( - attrs={"aria-describedby": "desc_series_number"} - ), "subjects": ArrayWidget(), "languages": forms.TextInput( attrs={"aria-describedby": "desc_languages_help desc_languages"} @@ -116,7 +113,6 @@ class EditionFromWorkForm(CustomForm): "description", "languages", "series", - "series_number", "subjects", "subject_places", "cover", diff --git a/bookwyrm/migrations/0206_series_model.py b/bookwyrm/migrations/0206_series_model.py new file mode 100644 index 000000000..971230132 --- /dev/null +++ b/bookwyrm/migrations/0206_series_model.py @@ -0,0 +1,148 @@ +# Generated by Django 3.2.23 on 2024-02-04 20:27 + +import bookwyrm.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +def make_series(apps, schema_editor): + Edition = apps.get_model("bookwyrm", "Edition") + Series = apps.get_model("bookwyrm", "Series") + SeriesBook = apps.get_model("bookwyrm", "SeriesBook") + + db_alias = schema_editor.connection.alias + + with_series = ( + Edition.objects.using(db_alias) + .exclude(series_name__isnull=True) + .exclude(series_name__exact="") + .order_by("series_name", "series_number") + ) + for edition in with_series: + # TODO: Try to parse number from series_name if series_number is empty? + series, _ = Series.objects.using(db_alias).get_or_create( + name=edition.series_name, + authors=edition.authors.all(), + ) + SeriesBook.objects.using(db_alias).create( + book=edition, series=series, number=edition.series_number + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0205_merge_20240413_0232.py"), + ] + + operations = [ + migrations.RenameField( + model_name="book", + old_name="series", + new_name="series_name", + ), + migrations.CreateModel( + name="Series", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ( + "remote_id", + bookwyrm.models.fields.RemoteIdField( + max_length=255, + null=True, + validators=[bookwyrm.models.fields.validate_remote_id], + ), + ), + ("name", bookwyrm.models.fields.CharField(max_length=100)), + ( + "authors", + bookwyrm.models.fields.ManyToManyField(to="bookwyrm.Author"), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="SeriesBook", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ( + "remote_id", + bookwyrm.models.fields.RemoteIdField( + max_length=255, + null=True, + validators=[bookwyrm.models.fields.validate_remote_id], + ), + ), + ( + "number", + bookwyrm.models.fields.CharField( + blank=True, max_length=255, null=True + ), + ), + ( + "book", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="bookwyrm.book" + ), + ), + ( + "series", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="bookwyrm.series", + ), + ), + ], + options={ + "ordering": ["-number"], + "unique_together": {("book", "series")}, + }, + ), + migrations.AddField( + model_name="series", + name="books", + field=bookwyrm.models.fields.ManyToManyField( + related_name="series_books", + through="bookwyrm.SeriesBook", + to="bookwyrm.Book", + ), + ), + migrations.AddField( + model_name="book", + name="series", + field=models.ManyToManyField( + through="bookwyrm.SeriesBook", to="bookwyrm.Series" + ), + ), + migrations.RunPython(make_series), # TODO: reverse_code + migrations.RemoveField( + model_name="book", + name="series_number", + ), + migrations.RemoveField( + model_name="book", + name="series_name", + ), + ] diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index 6bb99c7f2..93017303a 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -1,4 +1,5 @@ """ bring all the models into the app namespace """ + import inspect import sys @@ -7,6 +8,8 @@ from .author import Author from .link import Link, FileLink, LinkDomain from .connector import Connector +from .series import Series, SeriesBook + from .shelf import Shelf, ShelfBook from .list import List, ListItem diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 8e957b717..8fae3f752 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -229,8 +229,11 @@ class Book(BookDataModel): languages = fields.ArrayField( models.CharField(max_length=255), blank=True, default=list ) - series = fields.TextField(max_length=255, blank=True, null=True) - series_number = fields.CharField(max_length=255, blank=True, null=True) + series = models.ManyToManyField( + "Series", + through="SeriesBook", + through_fields=("book", "series"), + ) subjects = fields.ArrayField( models.CharField(max_length=255), blank=True, null=True, default=list ) diff --git a/bookwyrm/models/series.py b/bookwyrm/models/series.py new file mode 100644 index 000000000..55beae95d --- /dev/null +++ b/bookwyrm/models/series.py @@ -0,0 +1,35 @@ +"""series of books""" + +from django.db import models + +from .base_model import BookWyrmModel +from . import fields + + +class Series(BookWyrmModel): + """a named series of books""" + + name = fields.CharField(max_length=100) + authors = fields.ManyToManyField("Author") # TODO: add on Author model + books = fields.ManyToManyField( + "Book", + through="SeriesBook", + through_fields=("series", "book"), + related_name="series_books", + ) + + +class SeriesBook(BookWyrmModel): + """membership of a series""" + + book = models.ForeignKey("Book", on_delete=models.PROTECT) + series = models.ForeignKey("Series", on_delete=models.PROTECT) + number = fields.CharField(max_length=255, blank=True, null=True) + + collection_field = "series" + + class Meta: + """a series can't contain the same book twice""" + + unique_together = ("book", "series") + ordering = ["-number"] diff --git a/bookwyrm/templates/book/book.html b/bookwyrm/templates/book/book.html index 83500a54b..77bef4ebc 100644 --- a/bookwyrm/templates/book/book.html +++ b/bookwyrm/templates/book/book.html @@ -31,7 +31,7 @@ {{ book.title }} - {% if book.subtitle or book.series %} + {% if book.subtitle or book.series.exists %}

{% if book.subtitle %} {% endif %} - {% if book.series %} + {% for book_series in book.series.all %} {% spaceless %} - {% if book.authors.exists %} - - {% endif %} + {% if book_series.number %} + , # + {{ book.series_number }} + {% endif %} - {% if book.series_number %} - , # - {{ book.series_number }} - {% endif %} {% endspaceless %} - {% endif %} + {% endfor %}

{% endif %}