""" puttin' books on shelves """ import re from django.db import models from bookwyrm import activitypub from .activitypub_mixin import CollectionItemMixin, OrderedCollectionMixin from .base_model import BookWyrmModel from . import fields class Shelf(OrderedCollectionMixin, BookWyrmModel): """ a list of books owned by a user """ TO_READ = "to-read" READING = "reading" READ_FINISHED = "read" READ_STATUS_IDENTIFIERS = (TO_READ, READING, READ_FINISHED) name = fields.CharField(max_length=100) identifier = models.CharField(max_length=100) user = fields.ForeignKey( "User", on_delete=models.PROTECT, activitypub_field="owner" ) editable = models.BooleanField(default=True) privacy = fields.PrivacyField() books = models.ManyToManyField( "Edition", symmetrical=False, through="ShelfBook", through_fields=("shelf", "book"), ) activity_serializer = activitypub.Shelf collection_field = "shelf" def save(self, *args, **kwargs): """ set the identifier """ super().save(*args, **kwargs) if not self.identifier: self.identifier = self.get_identifier() super().save(*args, **kwargs, broadcast=False) def get_identifier(self): """ custom-shelf-123 for the url """ slug = re.sub(r"[^\w]", "", self.name).lower() return "{:s}-{:d}".format(slug, self.id) @property def collection_queryset(self): """ list of books for this shelf, overrides OrderedCollectionMixin """ return self.books.all().order_by("shelfbook") def get_remote_id(self): """ shelf identifier instead of id """ base_path = self.user.remote_id identifier = self.identifier or self.get_identifier() return "%s/books/%s" % (base_path, identifier) class Meta: """ user/shelf unqiueness """ unique_together = ("user", "identifier") class ShelfBook(CollectionItemMixin, BookWyrmModel): """ many to many join table for books and shelves """ book = fields.ForeignKey( "Edition", on_delete=models.PROTECT, activitypub_field="object" ) shelf = fields.ForeignKey( "Shelf", on_delete=models.PROTECT, activitypub_field="target" ) user = fields.ForeignKey( "User", on_delete=models.PROTECT, activitypub_field="actor" ) activity_serializer = activitypub.ShelfItem def save(self, *args, **kwargs): if not self.user: self.user = self.shelf.user super().save(*args, **kwargs) class Meta: """an opinionated constraint! you can't put a book on shelf twice""" unique_together = ("book", "shelf") ordering = ("-created_date",)