First pass at recursively resolving foreign keys

This commit is contained in:
Mouse Reeve 2020-11-27 22:10:38 -08:00
parent 5638597112
commit ebb82287c2
3 changed files with 36 additions and 28 deletions

View file

@ -5,7 +5,6 @@ import sys
from .base_activity import ActivityEncoder, PublicKey, Signature from .base_activity import ActivityEncoder, PublicKey, Signature
from .base_activity import Link, Mention from .base_activity import Link, Mention
from .base_activity import ActivitySerializerError from .base_activity import ActivitySerializerError
from .base_activity import tag_formatter
from .image import Image from .image import Image
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
from .note import Tombstone from .note import Tombstone

View file

@ -25,12 +25,13 @@ class ActivityEncoder(JSONEncoder):
@dataclass @dataclass
class Link(): class Link:
''' for tagging a book in a status ''' ''' for tagging a book in a status '''
href: str href: str
name: str name: str
type: str = 'Link' type: str = 'Link'
@dataclass @dataclass
class Mention(Link): class Mention(Link):
''' a subtype of Link for mentioning an actor ''' ''' a subtype of Link for mentioning an actor '''
@ -125,7 +126,7 @@ class ActivityObject:
with transaction.atomic(): with transaction.atomic():
if instance: if instance:
# updating an existing model isntance # updating an existing model instance
for k, v in mapped_fields.items(): for k, v in mapped_fields.items():
setattr(instance, k, v) setattr(instance, k, v)
instance.save() instance.save()
@ -133,6 +134,9 @@ class ActivityObject:
# creating a new model instance # creating a new model instance
instance = model.objects.create(**mapped_fields) instance = model.objects.create(**mapped_fields)
# --- these are all fields that can't be saved until after the
# instance has an id (after it's been saved). ---------------#
# add images # add images
for (model_key, value) in image_fields.items(): for (model_key, value) in image_fields.items():
formatted_value = image_formatter(value) formatted_value = image_formatter(value)
@ -140,9 +144,20 @@ class ActivityObject:
continue continue
getattr(instance, model_key).save(*formatted_value, save=True) getattr(instance, model_key).save(*formatted_value, save=True)
# add many to many fields
for (model_key, values) in many_to_many_fields.items(): for (model_key, values) in many_to_many_fields.items():
# mention books, mention users # mention books, mention users
getattr(instance, model_key).set(values) if values == MISSING:
continue
model_field = getattr(instance, model_key)
model = model_field.model
items = []
for link in values:
items.append(
resolve_foreign_key(model, link.get('href'))
)
getattr(instance, model_key).set(items)
# add one to many fields # add one to many fields
for (model_key, values) in one_to_many_fields.items(): for (model_key, values) in one_to_many_fields.items():
@ -177,36 +192,30 @@ def resolve_foreign_key(model, remote_id):
if hasattr(model.objects, 'select_subclasses'): if hasattr(model.objects, 'select_subclasses'):
result = result.select_subclasses() result = result.select_subclasses()
# first, check for an existing copy in the database
result = result.filter( result = result.filter(
remote_id=remote_id remote_id=remote_id
).first() ).first()
if result:
return result
if not result: # failing that, load the data and create the object
try:
response = requests.get(
remote_id,
headers={'Accept': 'application/json; charset=utf-8'},
)
except ConnectionError:
raise ActivitySerializerError(
'Could not connect to host for remote_id in %s model: %s' % \
(model.__name__, remote_id))
if not response.ok:
raise ActivitySerializerError( raise ActivitySerializerError(
'Could not resolve remote_id in %s model: %s' % \ 'Could not resolve remote_id in %s model: %s' % \
(model.__name__, remote_id)) (model.__name__, remote_id))
return result
item = model.activity_serializer(**response.json())
def tag_formatter(tags, tag_type): return item.to_model(model)
''' helper function to extract foreign keys from tag activity json '''
if not isinstance(tags, list):
return []
items = []
types = {
'Book': models.Book,
'Mention': models.User,
}
for tag in [t for t in tags if t.get('type') == tag_type]:
if not tag_type in types:
continue
remote_id = tag.get('href')
try:
item = resolve_foreign_key(types[tag_type], remote_id)
except ActivitySerializerError:
continue
items.append(item)
return items
def image_formatter(image_slug): def image_formatter(image_slug):

View file

@ -80,12 +80,12 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
ActivityMapping( ActivityMapping(
'tag', 'mention_books', 'tag', 'mention_books',
lambda x: tag_formatter(x, 'title', 'Book'), lambda x: tag_formatter(x, 'title', 'Book'),
lambda x: activitypub.tag_formatter(x, 'Book') lambda x: [i for i in x if x.get('type') == 'Book']
), ),
ActivityMapping( ActivityMapping(
'tag', 'mention_users', 'tag', 'mention_users',
lambda x: tag_formatter(x, 'username', 'Mention'), lambda x: tag_formatter(x, 'username', 'Mention'),
lambda x: activitypub.tag_formatter(x, 'Mention') lambda x: [i for i in x if x.get('type') == 'Mention']
), ),
ActivityMapping( ActivityMapping(
'attachment', 'attachments', 'attachment', 'attachments',