''' 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 ''' 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 def save(self, *args, **kwargs): ''' set the identifier ''' super().save(*args, **kwargs) if not self.identifier: slug = re.sub(r'[^\w]', '', self.name).lower() self.identifier = '%s-%d' % (slug, self.id) super().save(*args, **kwargs) @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 return '%s/shelf/%s' % (base_path, self.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', blank=True, null=True, on_delete=models.PROTECT, activitypub_field='actor' ) activity_serializer = activitypub.AddBook object_field = 'book' collection_field = 'shelf' class Meta: ''' an opinionated constraint! you can't put a book on shelf twice ''' unique_together = ('book', 'shelf') ordering = ('-created_date',)