Add new Block.render_api method (#3237)

Add new Block.get_api_representation method
pull/3302/head
Marco Fucci 2017-01-26 17:14:34 +00:00 zatwierdzone przez Karl Hobley
rodzic e77da0a1a1
commit c606e3f722
9 zmienionych plików z 170 dodań i 2 usunięć

Wyświetl plik

@ -225,7 +225,7 @@ class StreamField(Field):
foreign objects are nested objects with id and meta as attributes.
"""
def to_representation(self, value):
return value.stream_block.get_prep_value(value)
return value.stream_block.get_api_representation(value, self.context)
class TagsField(Field):

Wyświetl plik

@ -1025,6 +1025,18 @@ class TestPageDetailWithStreamField(TestCase):
# ForeignKeys in a StreamField shouldn't be translated into dictionary representation
self.assertEqual(content['body'], [{'type': 'image', 'value': 1}])
def test_image_block_with_custom_get_api_representation(self):
stream_page = self.make_stream_page('[{"type": "image", "value": 1}]')
response_url = '{}?extended=1'.format(
reverse('wagtailapi_v2:pages:detail', args=(stream_page.id, ))
)
response = self.client.get(response_url)
content = json.loads(response.content.decode('utf-8'))
# the custom get_api_representation returns a dict of id and title for the image
self.assertEqual(content['body'], [{'type': 'image', 'value': {'id': 1, 'title': 'A missing image'}}])
@override_settings(
WAGTAILFRONTENDCACHE={

Wyświetl plik

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-20 10:57
from __future__ import unicode_literals
from django.db import migrations
import wagtail.tests.testapp.models
import wagtail.wagtailcore.blocks
import wagtail.wagtailcore.fields
class Migration(migrations.Migration):
dependencies = [
('tests', '0012_panelsettings_tabbedsettings'),
]
operations = [
migrations.AlterField(
model_name='streampage',
name='body',
field=wagtail.wagtailcore.fields.StreamField([('text', wagtail.wagtailcore.blocks.CharBlock()), ('rich_text', wagtail.wagtailcore.blocks.RichTextBlock()), ('image', wagtail.tests.testapp.models.ExtendedImageChooserBlock())]),
),
]

Wyświetl plik

@ -638,11 +638,27 @@ class StreamModel(models.Model):
])
class ExtendedImageChooserBlock(ImageChooserBlock):
"""
Example of Block with custom get_api_representation method.
If the request has an 'extended' query param, it returns a dict of id and title,
otherwise, it returns the default value.
"""
def get_api_representation(self, value, context=None):
image_id = super(ExtendedImageChooserBlock, self).get_api_representation(value, context=context)
if 'request' in context and context['request'].query_params.get('extended', False):
return {
'id': image_id,
'title': value.title
}
return image_id
class StreamPage(Page):
body = StreamField([
('text', CharBlock()),
('rich_text', RichTextBlock()),
('image', ImageChooserBlock()),
('image', ExtendedImageChooserBlock()),
])
api_fields = ('body',)

Wyświetl plik

@ -257,6 +257,12 @@ class Block(six.with_metaclass(BaseBlock, object)):
return mark_safe(render_to_string(template, new_context))
def get_api_representation(self, value, context=None):
"""
Can be used to customise the API response and defaults to the value returned by get_prep_value.
"""
return self.get_prep_value(value)
def render_basic(self, value, context=None):
"""
Return a text rendering of 'value', suitable for display on templates. render() will fall back on

Wyświetl plik

@ -143,6 +143,13 @@ class ListBlock(Block):
for item in value
]
def get_api_representation(self, value, context=None):
# recursively call get_api_representation on children and return as a list
return [
self.child_block.get_api_representation(item, context=context)
for item in value
]
def render_basic(self, value, context=None):
children = format_html_join(
'\n', '<li>{0}</li>',

Wyświetl plik

@ -211,6 +211,16 @@ class BaseStreamBlock(Block):
for child in value # child is a BoundBlock instance
]
def get_api_representation(self, value, context=None):
if value is None:
# treat None as identical to an empty stream
return []
return [
{'type': child.block.name, 'value': child.block.get_api_representation(child.value, context=context)}
for child in value # child is a BoundBlock instance
]
def render_basic(self, value, context=None):
return format_html_join(
'\n', '<div class="block-{1}">{0}</div>',

Wyświetl plik

@ -138,6 +138,13 @@ class BaseStructBlock(Block):
for name, val in value.items()
])
def get_api_representation(self, value, context=None):
# recursively call get_api_representation on children and return as a plain dict
return dict([
(name, self.child_blocks[name].get_api_representation(val, context=context))
for name, val in value.items()
])
def get_searchable_content(self, value):
content = []

Wyświetl plik

@ -961,6 +961,39 @@ class TestStructBlock(SimpleTestCase):
self.assertHTMLEqual(html, expected_html)
def test_get_api_representation_calls_same_method_on_fields_with_context(self):
"""
The get_api_representation method of a StructBlock should invoke
the block's get_api_representation method on each field and the
context should be passed on.
"""
class ContextBlock(blocks.CharBlock):
def get_api_representation(self, value, context=None):
return context[value]
class AuthorBlock(blocks.StructBlock):
language = ContextBlock()
author = ContextBlock()
block = AuthorBlock()
api_representation = block.get_api_representation(
{
'language': 'en',
'author': 'wagtail',
},
context={
'en': 'English',
'wagtail': 'Wagtail!'
}
)
self.assertDictEqual(
api_representation, {
'language': 'English',
'author': 'Wagtail!'
}
)
def test_render_unknown_field(self):
class LinkBlock(blocks.StructBlock):
title = blocks.CharBlock()
@ -1334,6 +1367,28 @@ class TestListBlock(unittest.TestCase):
self.assertIn('<h1 lang="fr">Bonjour le monde!</h1>', html)
self.assertIn('<h1 lang="fr">Au revoir le monde!</h1>', html)
def test_get_api_representation_calls_same_method_on_children_with_context(self):
"""
The get_api_representation method of a ListBlock should invoke
the block's get_api_representation method on each child and
the context should be passed on.
"""
class ContextBlock(blocks.CharBlock):
def get_api_representation(self, value, context=None):
return context[value]
block = blocks.ListBlock(
ContextBlock()
)
api_representation = block.get_api_representation(["en", "fr"], context={
'en': 'Hello world!',
'fr': 'Bonjour le monde!'
})
self.assertEqual(
api_representation, ['Hello world!', 'Bonjour le monde!']
)
def render_form(self):
class LinkBlock(blocks.StructBlock):
title = blocks.CharBlock()
@ -1652,6 +1707,38 @@ class TestStreamBlock(SimpleTestCase):
return block.render(value)
def test_get_api_representation_calls_same_method_on_children_with_context(self):
"""
The get_api_representation method of a StreamBlock should invoke
the block's get_api_representation method on each child and
the context should be passed on.
"""
class ContextBlock(blocks.CharBlock):
def get_api_representation(self, value, context=None):
return context[value]
block = blocks.StreamBlock([
('language', ContextBlock()),
('author', ContextBlock()),
])
api_representation = block.get_api_representation(
block.to_python([
{'type': 'language', 'value': 'en'},
{'type': 'author', 'value': 'wagtail'},
]),
context={
'en': 'English',
'wagtail': 'Wagtail!'
}
)
self.assertListEqual(
api_representation, [
{'type': 'language', 'value': 'English'},
{'type': 'author', 'value': 'Wagtail!'},
]
)
def test_render(self):
html = self.render_article([
{