kopia lustrzana https://github.com/wagtail/wagtail
Rewrite wagtail.core.telepath to recursively pack constructor args
js_args and get_media methods no longer receive the context object, since they are not responsible for packing their own arguments.pull/6931/head
rodzic
ac0c5d7ef5
commit
d4dc02ad0f
|
@ -86,7 +86,7 @@ class DraftailRichTextArea(widgets.HiddenInput):
|
|||
class DraftailRichTextAreaAdapter(WidgetAdapter):
|
||||
js_constructor = 'wagtail.widgets.DraftailRichTextArea'
|
||||
|
||||
def js_args(self, widget, context):
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.options,
|
||||
]
|
||||
|
|
|
@ -173,7 +173,7 @@ class AdminPageChooser(AdminChooser):
|
|||
class PageChooserAdapter(WidgetAdapter):
|
||||
js_constructor = 'wagtail.widgets.PageChooser'
|
||||
|
||||
def js_args(self, widget, context):
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.render_html('__NAME__', None, attrs={'id': '__ID__'}),
|
||||
widget.id_for_label('__ID__'),
|
||||
|
|
|
@ -74,7 +74,7 @@ class FieldBlock(Block):
|
|||
class FieldBlockAdapter(Adapter):
|
||||
js_constructor = 'wagtail.blocks.FieldBlock'
|
||||
|
||||
def js_args(self, block, context):
|
||||
def js_args(self, block):
|
||||
classname = [
|
||||
'field',
|
||||
camelcase_to_underscore(block.field.__class__.__name__),
|
||||
|
@ -85,7 +85,7 @@ class FieldBlockAdapter(Adapter):
|
|||
|
||||
return [
|
||||
block.name,
|
||||
context.pack(block.field.widget),
|
||||
block.field.widget,
|
||||
{
|
||||
'label': block.label,
|
||||
'required': block.required,
|
||||
|
|
|
@ -143,10 +143,10 @@ class ListBlock(Block):
|
|||
class ListBlockAdapter(Adapter):
|
||||
js_constructor = 'wagtail.blocks.ListBlock'
|
||||
|
||||
def js_args(self, block, context):
|
||||
def js_args(self, block):
|
||||
return [
|
||||
block.name,
|
||||
context.pack(block.child_block),
|
||||
block.child_block,
|
||||
{
|
||||
'label': block.label, 'icon': block.meta.icon, 'classname': block.meta.form_classname,
|
||||
'helpText': getattr(block.meta, 'help_text', None),
|
||||
|
|
|
@ -611,10 +611,10 @@ class StreamValue(MutableSequence):
|
|||
class StreamBlockAdapter(Adapter):
|
||||
js_constructor = 'wagtail.blocks.StreamBlock'
|
||||
|
||||
def js_args(self, block, context):
|
||||
def js_args(self, block):
|
||||
return [
|
||||
block.name,
|
||||
[context.pack(child) for child in block.child_blocks.values()],
|
||||
block.child_blocks.values(),
|
||||
{
|
||||
'label': block.label, 'required': block.required, 'icon': block.meta.icon,
|
||||
'classname': block.meta.form_classname, 'helpText': getattr(block.meta, 'help_text', None),
|
||||
|
|
|
@ -214,10 +214,10 @@ class StructBlock(BaseStructBlock, metaclass=DeclarativeSubBlocksMetaclass):
|
|||
class StructBlockAdapter(Adapter):
|
||||
js_constructor = 'wagtail.blocks.StructBlock'
|
||||
|
||||
def js_args(self, block, context):
|
||||
def js_args(self, block):
|
||||
return [
|
||||
block.name,
|
||||
[context.pack(child) for child in block.child_blocks.values()],
|
||||
block.child_blocks.values(),
|
||||
{
|
||||
'label': block.label, 'required': block.required, 'icon': block.meta.icon,
|
||||
'classname': block.meta.form_classname, 'helpText': getattr(block.meta, 'help_text', None),
|
||||
|
|
|
@ -4,7 +4,73 @@ from django.forms import MediaDefiningClass
|
|||
from wagtail.admin.staticfiles import versioned_static
|
||||
|
||||
|
||||
adapters = {}
|
||||
DICT_RESERVED_KEYS = ['_type', '_args', '_dict']
|
||||
|
||||
|
||||
class BaseAdapter:
|
||||
"""Handles serialisation of a specific object type"""
|
||||
def pack(self, obj, context):
|
||||
"""
|
||||
Translates obj into serialisable form. Any media declarations that will be required for
|
||||
deserialisation of the object should be passed to context.add_media().
|
||||
|
||||
This base implementation handles simple JSON-serialisable values such as strings, and
|
||||
returns them unchanged.
|
||||
"""
|
||||
return obj
|
||||
|
||||
|
||||
class DictAdapter(BaseAdapter):
|
||||
"""Handles serialisation of dicts"""
|
||||
def pack(self, obj, context):
|
||||
packed_obj = {
|
||||
str(key): context.pack(val)
|
||||
for key, val in obj.items()
|
||||
}
|
||||
if any(reserved_key in packed_obj for reserved_key in DICT_RESERVED_KEYS):
|
||||
# this dict contains keys such as _type that would collide with our object notation,
|
||||
# so wrap it in an explicit _dict to disambiguate
|
||||
return {'_dict': packed_obj}
|
||||
else:
|
||||
return packed_obj
|
||||
|
||||
|
||||
class Adapter(BaseAdapter, metaclass=MediaDefiningClass):
|
||||
"""
|
||||
Handles serialisation of custom types.
|
||||
Subclasses should define:
|
||||
- js_constructor: namespaced identifier for the JS constructor function that will unpack this
|
||||
object
|
||||
- js_args(obj): returns a list of (telepath-packable) arguments to be passed to the constructor
|
||||
- get_media(obj) or class Media: media definitions necessary for unpacking
|
||||
|
||||
The adapter should then be registered with register(adapter, cls).
|
||||
"""
|
||||
|
||||
def get_media(self, obj):
|
||||
return self.media
|
||||
|
||||
def pack(self, obj, context):
|
||||
context.add_media(self.get_media(obj))
|
||||
return {
|
||||
'_type': self.js_constructor,
|
||||
'_args': [context.pack(arg) for arg in self.js_args(obj)]
|
||||
}
|
||||
|
||||
|
||||
adapters = {
|
||||
# Primitive value types that are unchanged on serialisation
|
||||
type(None): BaseAdapter(),
|
||||
bool: BaseAdapter(),
|
||||
int: BaseAdapter(),
|
||||
float: BaseAdapter(),
|
||||
str: BaseAdapter(),
|
||||
|
||||
# Container types to be serialised recursively
|
||||
dict: DictAdapter(),
|
||||
# Iterable types (list, tuple, odict_values...) do not have a reliably recognisable
|
||||
# superclass, so will be handled as a special case
|
||||
}
|
||||
|
||||
|
||||
def register(adapter, cls):
|
||||
|
@ -16,21 +82,27 @@ class JSContext:
|
|||
self.media = forms.Media(js=[
|
||||
versioned_static('wagtailadmin/js/telepath/telepath.js')
|
||||
])
|
||||
self.objects = {}
|
||||
|
||||
# Keep track of media declarations that have already added to self.media - ones that
|
||||
# exactly match a previous one can be ignored, as they will not affect the result
|
||||
self.media_fragments = set([str(self.media)])
|
||||
|
||||
def add_media(self, media):
|
||||
media_str = str(media)
|
||||
if media_str not in self.media_fragments:
|
||||
self.media += media
|
||||
self.media_fragments.add(media_str)
|
||||
|
||||
def pack(self, obj):
|
||||
for cls in type(obj).__mro__:
|
||||
adapter = adapters.get(cls)
|
||||
if adapter:
|
||||
break
|
||||
return adapter.pack(obj, self)
|
||||
|
||||
if adapter is None:
|
||||
raise Exception("don't know how to add object to JS context: %r" % obj)
|
||||
# as fallback, try handling as an iterable
|
||||
try:
|
||||
return [self.pack(item) for item in obj]
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.media += adapter.get_media(obj, self)
|
||||
return [adapter.js_constructor, *adapter.js_args(obj, self)]
|
||||
|
||||
|
||||
class Adapter(metaclass=MediaDefiningClass):
|
||||
def get_media(self, obj, context):
|
||||
return self.media
|
||||
raise Exception("don't know how to pack object: %r" % obj)
|
||||
|
|
|
@ -13,14 +13,14 @@ from wagtail.core.telepath import Adapter, register
|
|||
class WidgetAdapter(Adapter):
|
||||
js_constructor = 'wagtail.widgets.Widget'
|
||||
|
||||
def js_args(self, widget, context):
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.render('__NAME__', None, attrs={'id': '__ID__'}),
|
||||
widget.id_for_label('__ID__'),
|
||||
]
|
||||
|
||||
def get_media(self, widget, context):
|
||||
media = super().get_media(widget, context)
|
||||
def get_media(self, widget):
|
||||
media = super().get_media(widget)
|
||||
return media + widget.media
|
||||
|
||||
class Media:
|
||||
|
|
|
@ -62,7 +62,7 @@ class AdminDocumentChooser(AdminChooser):
|
|||
class DocumentChooserAdapter(WidgetAdapter):
|
||||
js_constructor = 'wagtail.documents.widgets.DocumentChooser'
|
||||
|
||||
def js_args(self, widget, context):
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.render_html('__NAME__', None, attrs={'id': '__ID__'}),
|
||||
widget.id_for_label('__ID__'),
|
||||
|
|
|
@ -71,7 +71,7 @@ class AdminImageChooser(AdminChooser):
|
|||
class ImageChooserAdapter(WidgetAdapter):
|
||||
js_constructor = 'wagtail.images.widgets.ImageChooser'
|
||||
|
||||
def js_args(self, widget, context):
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.render_html('__NAME__', None, attrs={'id': '__ID__'}),
|
||||
widget.id_for_label('__ID__'),
|
||||
|
|
|
@ -79,7 +79,7 @@ class AdminSnippetChooser(AdminChooser):
|
|||
class SnippetChooserAdapter(WidgetAdapter):
|
||||
js_constructor = 'wagtail.snippets.widgets.SnippetChooser'
|
||||
|
||||
def js_args(self, widget, context):
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.render_html('__NAME__', None, attrs={'id': '__ID__'}),
|
||||
widget.id_for_label('__ID__'), widget.model_string
|
||||
|
|
Ładowanie…
Reference in New Issue