diff --git a/bookwyrm/activitypub/book.py b/bookwyrm/activitypub/book.py index 597d7a665..1599b408a 100644 --- a/bookwyrm/activitypub/book.py +++ b/bookwyrm/activitypub/book.py @@ -62,7 +62,6 @@ class Work(Book): """work instance of a book object""" lccn: str = "" - defaultEdition: str = "" editions: List[str] = field(default_factory=lambda: []) type: str = "Work" diff --git a/bookwyrm/connectors/abstract_connector.py b/bookwyrm/connectors/abstract_connector.py index 48c051823..08a0175a8 100644 --- a/bookwyrm/connectors/abstract_connector.py +++ b/bookwyrm/connectors/abstract_connector.py @@ -116,8 +116,8 @@ class AbstractConnector(AbstractMinimalConnector): remote_id ) or models.Work.find_existing_by_remote_id(remote_id) if existing: - if hasattr(existing, "get_default_editon"): - return existing.get_default_editon() + if hasattr(existing, "default_edition"): + return existing.default_edition return existing # load the json @@ -170,10 +170,6 @@ class AbstractConnector(AbstractMinimalConnector): edition.connector = self.connector edition.save() - if not work.default_edition: - work.default_edition = edition - work.save() - for author in self.get_authors_from_data(edition_data): edition.authors.add(author) if not edition.authors.exists() and work.authors.exists(): diff --git a/bookwyrm/connectors/bookwyrm_connector.py b/bookwyrm/connectors/bookwyrm_connector.py index 640a0bca7..6b1d2f8ca 100644 --- a/bookwyrm/connectors/bookwyrm_connector.py +++ b/bookwyrm/connectors/bookwyrm_connector.py @@ -6,12 +6,8 @@ from .abstract_connector import AbstractMinimalConnector, SearchResult class Connector(AbstractMinimalConnector): """this is basically just for search""" - def get_or_create_book(self, remote_id): - edition = activitypub.resolve_remote_id(remote_id, model=models.Edition) - work = edition.parent_work - work.default_edition = work.get_default_edition() - work.save() - return edition + def get_or_create_book(self, remote_id, work=None): + return activitypub.resolve_remote_id(remote_id, model=models.Edition) def parse_search_data(self, data): return data diff --git a/bookwyrm/connectors/self_connector.py b/bookwyrm/connectors/self_connector.py index 0dc922a5a..6b1b349fe 100644 --- a/bookwyrm/connectors/self_connector.py +++ b/bookwyrm/connectors/self_connector.py @@ -3,7 +3,7 @@ from functools import reduce import operator from django.contrib.postgres.search import SearchRank, SearchVector -from django.db.models import Count, F, Q +from django.db.models import Count, OuterRef, Subquery, F, Q from bookwyrm import models from .abstract_connector import AbstractConnector, SearchResult @@ -47,7 +47,16 @@ class Connector(AbstractConnector): # when there are multiple editions of the same work, pick the default. # it would be odd for this to happen. - results = results.filter(parent_work__default_edition__id=F("id")) or results + + default_editions = models.Edition.objects.filter( + parent_work=OuterRef("parent_work") + ).order_by("-edition_rank") + results = ( + results.annotate( + default_id=Subquery(default_editions.values("id")[:1]) + ).filter(default_id=F("id")) + or results + ) search_results = [] for result in results: @@ -112,7 +121,15 @@ def search_identifiers(query, *filters): # when there are multiple editions of the same work, pick the default. # it would be odd for this to happen. - return results.filter(parent_work__default_edition__id=F("id")) or results + default_editions = models.Edition.objects.filter( + parent_work=OuterRef("parent_work") + ).order_by("-edition_rank") + return ( + results.annotate(default_id=Subquery(default_editions.values("id")[:1])).filter( + default_id=F("id") + ) + or results + ) def search_title_author(query, min_confidence, *filters): @@ -140,10 +157,10 @@ def search_title_author(query, min_confidence, *filters): for work_id in set(editions_of_work): editions = results.filter(parent_work=work_id) - default = editions.filter(parent_work__default_edition=F("id")) - default_rank = default.first().rank if default.exists() else 0 + default = editions.order_by("-edition_rank").first() + default_rank = default.rank if default else 0 # if mutliple books have the top rank, pick the default edition if default_rank == editions.first().rank: - yield default.first() + yield default else: yield editions.first() diff --git a/bookwyrm/migrations/0072_remove_work_default_edition.py b/bookwyrm/migrations/0072_remove_work_default_edition.py new file mode 100644 index 000000000..1c05c95e1 --- /dev/null +++ b/bookwyrm/migrations/0072_remove_work_default_edition.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2 on 2021-04-28 22:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0071_merge_0063_auto_20210407_0045_0070_auto_20210423_0121"), + ] + + operations = [ + migrations.RemoveField( + model_name="work", + name="default_edition", + ), + ] diff --git a/bookwyrm/models/book.py b/bookwyrm/models/book.py index 10ebb3178..0bbae0336 100644 --- a/bookwyrm/models/book.py +++ b/bookwyrm/models/book.py @@ -1,7 +1,7 @@ """ database schema for books and shelves """ import re -from django.db import models, transaction +from django.db import models from model_utils.managers import InheritanceManager from bookwyrm import activitypub @@ -143,10 +143,6 @@ class Work(OrderedCollectionPageMixin, Book): lccn = fields.CharField( max_length=255, blank=True, null=True, deduplication_field=True ) - # this has to be nullable but should never be null - default_edition = fields.ForeignKey( - "Edition", on_delete=models.PROTECT, null=True, load_remote=False - ) def save(self, *args, **kwargs): """set some fields on the edition object""" @@ -155,18 +151,10 @@ class Work(OrderedCollectionPageMixin, Book): edition.save() return super().save(*args, **kwargs) - def get_default_edition(self): + @property + def default_edition(self): """in case the default edition is not set""" - return self.default_edition or self.editions.order_by("-edition_rank").first() - - @transaction.atomic() - def reset_default_edition(self): - """sets a new default edition based on computed rank""" - self.default_edition = None - # editions are re-ranked implicitly - self.save() - self.default_edition = self.get_default_edition() - self.save() + return self.editions.order_by("-edition_rank").first() def to_edition_list(self, **kwargs): """an ordered collection of editions""" @@ -220,15 +208,8 @@ class Edition(Book): activity_serializer = activitypub.Edition name_field = "title" - def get_rank(self, ignore_default=False): + def get_rank(self): """calculate how complete the data is on this edition""" - if ( - not ignore_default - and self.parent_work - and self.parent_work.default_edition == self - ): - # default edition has the highest rank - return 20 rank = 0 rank += int(bool(self.cover)) * 3 rank += int(bool(self.isbn_13)) diff --git a/bookwyrm/tests/connectors/test_connector_manager.py b/bookwyrm/tests/connectors/test_connector_manager.py index feded6168..34abbeaf6 100644 --- a/bookwyrm/tests/connectors/test_connector_manager.py +++ b/bookwyrm/tests/connectors/test_connector_manager.py @@ -17,8 +17,6 @@ class ConnectorManager(TestCase): self.edition = models.Edition.objects.create( title="Example Edition", parent_work=self.work, isbn_10="0000000000" ) - self.work.default_edition = self.edition - self.work.save() self.connector = models.Connector.objects.create( identifier="test_connector", diff --git a/bookwyrm/tests/connectors/test_self_connector.py b/bookwyrm/tests/connectors/test_self_connector.py index eee7d00cf..db97b65a0 100644 --- a/bookwyrm/tests/connectors/test_self_connector.py +++ b/bookwyrm/tests/connectors/test_self_connector.py @@ -84,11 +84,11 @@ class SelfConnector(TestCase): title="Edition 1 Title", parent_work=work ) edition_2 = models.Edition.objects.create( - title="Edition 2 Title", parent_work=work + title="Edition 2 Title", + parent_work=work, + edition_rank=20, # that's default babey ) edition_3 = models.Edition.objects.create(title="Fish", parent_work=work) - work.default_edition = edition_2 - work.save() # pick the best edition results = self.connector.search("Edition 1 Title") diff --git a/bookwyrm/tests/models/test_book_model.py b/bookwyrm/tests/models/test_book_model.py index c80cc4a84..cad00d43a 100644 --- a/bookwyrm/tests/models/test_book_model.py +++ b/bookwyrm/tests/models/test_book_model.py @@ -84,9 +84,3 @@ class Book(TestCase): self.first_edition.description = "hi" self.first_edition.save() self.assertEqual(self.first_edition.edition_rank, 1) - - # default edition - self.work.default_edition = self.first_edition - self.work.save() - self.first_edition.refresh_from_db() - self.assertEqual(self.first_edition.edition_rank, 20) diff --git a/bookwyrm/tests/models/test_readthrough_model.py b/bookwyrm/tests/models/test_readthrough_model.py index 93e9e654c..986b739b6 100644 --- a/bookwyrm/tests/models/test_readthrough_model.py +++ b/bookwyrm/tests/models/test_readthrough_model.py @@ -2,7 +2,7 @@ from django.test import TestCase from django.core.exceptions import ValidationError -from bookwyrm import models, settings +from bookwyrm import models class ReadThrough(TestCase): @@ -19,8 +19,6 @@ class ReadThrough(TestCase): self.edition = models.Edition.objects.create( title="Example Edition", parent_work=self.work ) - self.work.default_edition = self.edition - self.work.save() self.readthrough = models.ReadThrough.objects.create( user=self.user, book=self.edition diff --git a/bookwyrm/tests/views/test_readthrough.py b/bookwyrm/tests/views/test_readthrough.py index c9ebf2169..882c79291 100644 --- a/bookwyrm/tests/views/test_readthrough.py +++ b/bookwyrm/tests/views/test_readthrough.py @@ -20,8 +20,6 @@ class ReadThrough(TestCase): self.edition = models.Edition.objects.create( title="Example Edition", parent_work=self.work ) - self.work.default_edition = self.edition - self.work.save() self.user = models.User.objects.create_user( "cinco", "cinco@example.com", "seissiete", local=True, localname="cinco" diff --git a/bookwyrm/views/author.py b/bookwyrm/views/author.py index 0bd7b0e04..41298161c 100644 --- a/bookwyrm/views/author.py +++ b/bookwyrm/views/author.py @@ -27,7 +27,7 @@ class Author(View): ).distinct() data = { "author": author, - "books": [b.get_default_edition() for b in books], + "books": [b.default_edition for b in books], } return TemplateResponse(request, "author.html", data) diff --git a/bookwyrm/views/books.py b/bookwyrm/views/books.py index 448cf9929..ee1a5e064 100644 --- a/bookwyrm/views/books.py +++ b/bookwyrm/views/books.py @@ -39,7 +39,7 @@ class Book(View): return ActivitypubResponse(book.to_activity()) if isinstance(book, models.Work): - book = book.get_default_edition() + book = book.default_edition if not book or not book.parent_work: return HttpResponseNotFound() diff --git a/bookwyrm/views/helpers.py b/bookwyrm/views/helpers.py index 8a60b54c7..540b578f4 100644 --- a/bookwyrm/views/helpers.py +++ b/bookwyrm/views/helpers.py @@ -123,7 +123,7 @@ def get_edition(book_id): """look up a book in the db and return an edition""" book = models.Book.objects.select_subclasses().get(id=book_id) if isinstance(book, models.Work): - book = book.get_default_edition() + book = book.default_edition return book