Create helper function on field for settings values

This commit is contained in:
Mouse Reeve 2020-12-13 12:02:26 -08:00
parent b67aea22fc
commit c470aeb3ce
3 changed files with 44 additions and 43 deletions

View file

@ -4,8 +4,6 @@ from json import JSONEncoder
from django.apps import apps
from django.db import transaction
from django.db.models.fields.files import ImageFileDescriptor
from django.db.models.fields.related_descriptors import ManyToManyDescriptor
from bookwyrm.connectors import ConnectorException, get_data
from bookwyrm.tasks import app
@ -77,55 +75,30 @@ class ActivityObject:
)
# check for an existing instance, if we're not updating a known obj
if not instance:
instance = model.find_existing(self.serialize()) or model()
instance = instance or model.find_existing(self.serialize()) or model()
many_to_many_fields = {}
image_fields = {}
for field in model._meta.get_fields():
# check if it's an activitypub field
if not hasattr(field, 'field_to_activity'):
continue
# call the formatter associated with the model field class
value = field.field_from_activity(
getattr(self, field.get_activitypub_field())
)
if value is None or value is MISSING:
continue
for field in instance.simple_fields:
field.set_field_from_activity(instance, self)
model_field = getattr(model, field.name)
if isinstance(model_field, ManyToManyDescriptor):
# status mentions book/users for example, stash this for later
many_to_many_fields[field.name] = value
elif isinstance(model_field, ImageFileDescriptor):
# image fields need custom handling
image_fields[field.name] = value
else:
# just a good old fashioned model.field = value
setattr(instance, field.name, value)
# if this isn't here, it messes up saving users. who even knows.
for (model_key, value) in image_fields.items():
getattr(instance, model_key).save(*value, save=save)
# image fields have to be set after other fields because they can save
# too early and jank up users
for field in instance.image_fields:
field.set_field_from_activity(instance, self, save=save)
if not save:
# we can't set many to many and reverse fields on an unsaved object
return instance
# we can't set many to many and reverse fields on an unsaved object
instance.save()
# add many to many fields, which have to be set post-save
for (model_key, values) in many_to_many_fields.items():
for field in instance.many_to_many_fields:
# mention books/users, for example
getattr(instance, model_key).set(values)
if not save or not hasattr(model, 'deserialize_reverse_fields'):
return instance
field.set_field_from_activity(instance, self)
# reversed relationships in the models
for (model_field_name, activity_field_name) in \
model.deserialize_reverse_fields:
instance.deserialize_reverse_fields:
# attachments on Status, for example
values = getattr(self, activity_field_name)
if values is None or values is MISSING:

View file

@ -10,13 +10,11 @@ from Crypto.Hash import SHA256
from django.core.paginator import Paginator
from django.db import models
from django.db.models import Q
from django.db.models.fields.files import ImageFileDescriptor
from django.db.models.fields.related_descriptors import ManyToManyDescriptor
from django.dispatch import receiver
from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN, PAGE_LENGTH
from .fields import RemoteIdField
from .fields import ImageField, ManyToManyField, RemoteIdField
PrivacyLevels = models.TextChoices('Privacy', [
@ -79,9 +77,9 @@ class ActivitypubMixin:
if not hasattr(field, 'field_to_activity'):
continue
if isinstance(field, ImageFileDescriptor):
if isinstance(field, ImageField):
self.image_fields.append(field)
elif isinstance(field, ManyToManyDescriptor):
elif isinstance(field, ManyToManyField):
self.many_to_many_fields.append(field)
else:
self.simple_fields.append(field)

View file

@ -1,4 +1,5 @@
''' activitypub-aware django model fields '''
from dataclasses import MISSING
import re
from uuid import uuid4
@ -38,6 +39,16 @@ class ActivitypubFieldMixin:
self.activitypub_field = activitypub_field
super().__init__(*args, **kwargs)
def set_field_from_activity(self, instance, data):
''' helper function for assinging a value to the field '''
value = getattr(data, self.get_activitypub_field())
formatted = self.field_from_activity(value)
if formatted is None or formatted is MISSING:
return
setattr(instance, self.name, formatted)
def field_to_activity(self, value):
''' formatter to convert a model value into activitypub '''
if hasattr(self, 'activitypub_wrapper'):
@ -145,6 +156,14 @@ class ManyToManyField(ActivitypubFieldMixin, models.ManyToManyField):
self.link_only = link_only
super().__init__(*args, **kwargs)
def set_field_from_activity(self, instance, data):
''' helper function for assinging a value to the field '''
value = getattr(data, self.get_activitypub_field())
formatted = self.field_from_activity(value)
if formatted is None or formatted is MISSING:
return
getattr(instance, self.name).set(formatted)
def field_to_activity(self, value):
if self.link_only:
return '%s/%s' % (value.instance.remote_id, self.name)
@ -210,9 +229,20 @@ def image_serializer(value):
class ImageField(ActivitypubFieldMixin, models.ImageField):
''' activitypub-aware image field '''
# pylint: disable=arguments-differ
def set_field_from_activity(self, instance, data, save=True):
''' helper function for assinging a value to the field '''
value = getattr(data, self.get_activitypub_field())
formatted = self.field_from_activity(value)
if formatted is None or formatted is MISSING:
return
getattr(instance, self.name).save(*formatted, save=save)
def field_to_activity(self, value):
return image_serializer(value)
def field_from_activity(self, value):
image_slug = value
# when it's an inline image (User avatar/icon, Book cover), it's a json