Preserve remote_id syntax for authors and books

This commit is contained in:
Mouse Reeve 2020-11-28 17:29:03 -08:00
parent 7ed2e310c0
commit 72c7829bab
5 changed files with 72 additions and 89 deletions

View File

@ -6,7 +6,6 @@ from uuid import uuid4
import dateutil.parser import dateutil.parser
from dateutil.parser import ParserError from dateutil.parser import ParserError
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.db import transaction
from django.db.models.fields.related_descriptors \ from django.db.models.fields.related_descriptors \
import ForwardManyToOneDescriptor, ManyToManyDescriptor, \ import ForwardManyToOneDescriptor, ManyToManyDescriptor, \
ReverseManyToOneDescriptor ReverseManyToOneDescriptor
@ -16,8 +15,6 @@ from django.db.models.query_utils import DeferredAttribute
from django.utils import timezone from django.utils import timezone
import requests import requests
from bookwyrm import models
class ActivitySerializerError(ValueError): class ActivitySerializerError(ValueError):
''' routine problems serializing activitypub json ''' ''' routine problems serializing activitypub json '''
@ -135,7 +132,7 @@ class ActivityObject:
# if the AP field is a serialized object (as in Add) # if the AP field is a serialized object (as in Add)
remote_id = formatted_value['id'] remote_id = formatted_value['id']
else: else:
# if the AP field is just a remote_id (as in every other case) # if the field is just a remote_id (as in every other case)
remote_id = formatted_value remote_id = formatted_value
reference = resolve_remote_id(fk_model, remote_id) reference = resolve_remote_id(fk_model, remote_id)
mapped_fields[mapping.model_key] = reference mapped_fields[mapping.model_key] = reference
@ -153,7 +150,6 @@ class ActivityObject:
formatted_value = None formatted_value = None
mapped_fields[mapping.model_key] = formatted_value mapped_fields[mapping.model_key] = formatted_value
with transaction.atomic():
if instance: if instance:
# updating an existing model instance # updating an existing model instance
for k, v in mapped_fields.items(): for k, v in mapped_fields.items():
@ -162,6 +158,9 @@ class ActivityObject:
else: else:
# creating a new model instance # creating a new model instance
instance = model.objects.create(**mapped_fields) instance = model.objects.create(**mapped_fields)
print('CREATING')
print(instance)
print(instance.id)
# --- these are all fields that can't be saved until after the # --- these are all fields that can't be saved until after the
# instance has an id (after it's been saved). ---------------# # instance has an id (after it's been saved). ---------------#
@ -203,10 +202,13 @@ class ActivityObject:
model_field = getattr(instance, model_key) model_field = getattr(instance, model_key)
model = model_field.model model = model_field.model
for item in values: for item in values:
if isinstance(item, str):
print(model)
item = resolve_remote_id(model, item)
else:
item = model.activity_serializer(**item) item = model.activity_serializer(**item)
field_name = instance.__class__.__name__.lower()
with transaction.atomic():
item = item.to_model(model) item = item.to_model(model)
field_name = instance.__class__.__name__.lower()
setattr(item, field_name, instance) setattr(item, field_name, instance)
item.save() item.save()

View File

@ -1,7 +1,4 @@
''' database schema for info about authors ''' ''' database schema for info about authors '''
from uuid import uuid4
import re
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
@ -37,18 +34,13 @@ class Author(ActivitypubMixin, BookWyrmModel):
self.remote_id = self.get_remote_id() self.remote_id = self.get_remote_id()
if not self.id: if not self.id:
# force set the remote id to a local version
self.origin_id = self.remote_id self.origin_id = self.remote_id
self.remote_id = self.get_remote_id() self.remote_id = None
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
def get_remote_id(self): def get_remote_id(self):
''' editions and works both use "book" instead of model_name ''' ''' editions and works both use "book" instead of model_name '''
uuid = str(uuid4())[:8] return 'https://%s/author/%s' % (DOMAIN, self.id)
# in Book, the title is used to make the url more readable, but
# since an author's name can change, I didn't want to lock in a
# potential deadname (or maiden name) in the urk.
return 'https://%s/author/%s' % (DOMAIN, uuid)
@property @property
def display_name(self): def display_name(self):

View File

@ -12,7 +12,7 @@ from Crypto.Hash import SHA256
from django.db import models from django.db import models
from django.db.models.fields.files import ImageFileDescriptor from django.db.models.fields.files import ImageFileDescriptor
from django.db.models.fields.related_descriptors \ from django.db.models.fields.related_descriptors \
import ManyToManyDescriptor import ManyToManyDescriptor, ReverseManyToOneDescriptor
from django.dispatch import receiver from django.dispatch import receiver
from bookwyrm import activitypub from bookwyrm import activitypub
@ -74,16 +74,18 @@ class ActivitypubMixin:
# this field on the model isn't serialized # this field on the model isn't serialized
continue continue
value = getattr(self, mapping.model_key) value = getattr(self, mapping.model_key)
model_field_type = getattr(self.__class__, mapping.model_key) model_field = getattr(self.__class__, mapping.model_key)
print(mapping.model_key, type(model_field))
if hasattr(value, 'remote_id'): if hasattr(value, 'remote_id'):
# this is probably a foreign key field, which we want to # this is probably a foreign key field, which we want to
# serialize as just the remote_id url reference # serialize as just the remote_id url reference
value = value.remote_id value = value.remote_id
elif isinstance(model_field_type, ManyToManyDescriptor): elif isinstance(model_field, ManyToManyDescriptor) or \
isinstance(model_field, ReverseManyToOneDescriptor):
value = [i.remote_id for i in value.all()] value = [i.remote_id for i in value.all()]
elif isinstance(value, datetime): elif isinstance(value, datetime):
value = value.isoformat() value = value.isoformat()
elif isinstance(model_field_type, ImageFileDescriptor): elif isinstance(model_field, ImageFileDescriptor):
value = image_formatter(value) value = image_formatter(value)
# run the custom formatter function set in the model # run the custom formatter function set in the model

View File

@ -1,5 +1,4 @@
''' database schema for books and shelves ''' ''' database schema for books and shelves '''
from uuid import uuid4
import re import re
from django.db import models from django.db import models
@ -99,16 +98,13 @@ class Book(ActivitypubMixin, BookWyrmModel):
self.remote_id = self.get_remote_id() self.remote_id = self.get_remote_id()
if not self.id: if not self.id:
# force set the remote id to a local version
self.origin_id = self.remote_id self.origin_id = self.remote_id
self.remote_id = self.get_remote_id() self.remote_id = None
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
def get_remote_id(self): def get_remote_id(self):
''' editions and works both use "book" instead of model_name ''' ''' editions and works both use "book" instead of model_name '''
uuid = str(uuid4())[:8] return 'https://%s/book/%d' % (DOMAIN, self.id)
clean_title = re.sub(r'[\W-]', '', self.title.replace(' ', '-')).lower()
return 'https://%s/author/%s-%s' % (DOMAIN, clean_title, uuid)
def __repr__(self): def __repr__(self):
return "<{} key={!r} title={!r}>".format( return "<{} key={!r} title={!r}>".format(
@ -129,17 +125,6 @@ class Work(OrderedCollectionPageMixin, Book):
null=True null=True
) )
def to_edition_list(self, **kwargs):
''' activitypub serialization for this work's editions '''
remote_id = self.remote_id + '/editions'
return self.to_ordered_collection(
self.edition_set,
remote_id=remote_id,
**kwargs
)
activity_serializer = activitypub.Work activity_serializer = activitypub.Work
@ -161,7 +146,8 @@ class Edition(Book):
through='ShelfBook', through='ShelfBook',
through_fields=('book', 'shelf') through_fields=('book', 'shelf')
) )
parent_work = models.ForeignKey('Work', on_delete=models.PROTECT, null=True) parent_work = models.ForeignKey(
'Work', on_delete=models.PROTECT, null=True, related_name='editions')
activity_serializer = activitypub.Edition activity_serializer = activitypub.Edition
@ -175,6 +161,7 @@ class Edition(Book):
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
def isbn_10_to_13(isbn_10): def isbn_10_to_13(isbn_10):
''' convert an isbn 10 into an isbn 13 ''' ''' convert an isbn 10 into an isbn 13 '''
isbn_10 = re.sub(r'[^0-9X]', '', isbn_10) isbn_10 = re.sub(r'[^0-9X]', '', isbn_10)

View File

@ -540,7 +540,7 @@ def book_page(request, book_id):
return HttpResponseNotFound() return HttpResponseNotFound()
reviews = models.Review.objects.filter( reviews = models.Review.objects.filter(
book__in=work.edition_set.all(), book__in=work.editions.all(),
) )
# all reviews for the book # all reviews for the book
reviews = get_activity_feed(request.user, 'federated', model=reviews) reviews = get_activity_feed(request.user, 'federated', model=reviews)