From ec38b13bd6662d593f094304cd23c0be2e4f6f02 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Tue, 1 Aug 2017 15:28:01 +0200 Subject: [PATCH 01/10] generic root_tag_name in assertXMLContains --- tests/test_renderers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 4168868..57c3c9e 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -120,4 +120,7 @@ def test_render_and_parse_complex_data(self): def assertXMLContains(self, xml, string): self.assertTrue(xml.startswith('\n')) self.assertTrue(xml.endswith('')) + def assertXMLContains(self, xml, string, root_tag='root'): + self.assertTrue(xml.startswith('\n<{0}>'.format(root_tag))) + self.assertTrue(xml.endswith(''.format(root_tag))) self.assertTrue(string in xml, '%r not in %r' % (string, xml)) From 6a5d0b86e8811dcb708371563a3b35bd42db238c Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Tue, 1 Aug 2017 15:30:10 +0200 Subject: [PATCH 02/10] add test data for new list-item feature --- tests/test_renderers.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 57c3c9e..1808d7e 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -33,6 +33,41 @@ class XMLRendererTestCase(TestCase): ] } + _complex_order_data = { + "creation_date": datetime.datetime(2017, 07, 01, 14, 30, 00), + "orderId": 1, + "positions": [ + { + "posNo": 1, + "amount": 3, + "messages": [ + { + "type": "O", + "code": "xyz" + }, + { + "type": "L", + "code": "zyx" + } + ] + }, + { + "posNo": 2, + "amount": 1, + "messages": [ + { + "type": "O", + "code": "xyz" + }, + { + "type": "L", + "code": "zyx" + } + ] + } + ] + } + def test_render_string(self): """ Test XML rendering. From 5cc262d32fb60b1aa1e85c4f0ce3b5a6f95c8258 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 09:05:14 +0200 Subject: [PATCH 03/10] add test for new list item feature --- tests/test_renderers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 1808d7e..e627ac4 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -139,6 +139,14 @@ def test_render_lazy(self): content = renderer.render({'field': lazy}, 'application/xml') self.assertXMLContains(content, 'hello') + def test_render_override_list_item(self): + renderer = XMLRenderer() + renderer.root_tag_name = 'order' + renderer.override_item_tag_name = True + content = renderer.render(self._complex_order_data, 'application/xml') + self.assertXMLContains(content, '', renderer.root_tag_name) + self.assertXMLContains(content, '', renderer.root_tag_name) + @skipUnless(etree, 'defusedxml not installed') def test_render_and_parse_complex_data(self): """ @@ -152,9 +160,6 @@ def test_render_and_parse_complex_data(self): error_msg = "complex data differs!IN:\n %s \n\n OUT:\n %s" % (repr(self._complex_data), repr(complex_data_out)) self.assertEqual(self._complex_data, complex_data_out, error_msg) - def assertXMLContains(self, xml, string): - self.assertTrue(xml.startswith('\n')) - self.assertTrue(xml.endswith('')) def assertXMLContains(self, xml, string, root_tag='root'): self.assertTrue(xml.startswith('\n<{0}>'.format(root_tag))) self.assertTrue(xml.endswith(''.format(root_tag))) From 5a62ed7f8f724ccb1669359a59c28844bc8ed3c0 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 09:06:25 +0200 Subject: [PATCH 04/10] impl. new list item feature --- rest_framework_xml/renderers.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rest_framework_xml/renderers.py b/rest_framework_xml/renderers.py index 66199a9..1715a8a 100644 --- a/rest_framework_xml/renderers.py +++ b/rest_framework_xml/renderers.py @@ -8,6 +8,7 @@ from django.utils.six.moves import StringIO from django.utils.encoding import force_text from rest_framework.renderers import BaseRenderer +from xml.etree import ElementTree as ET class XMLRenderer(BaseRenderer): @@ -20,6 +21,7 @@ class XMLRenderer(BaseRenderer): charset = 'utf-8' item_tag_name = 'list-item' root_tag_name = 'root' + override_item_tag_name = False def render(self, data, accepted_media_type=None, renderer_context=None): """ @@ -38,8 +40,23 @@ def render(self, data, accepted_media_type=None, renderer_context=None): xml.endElement(self.root_tag_name) xml.endDocument() + + if self.override_item_tag_name: + self._do_override_item_tag_name(stream) + return stream.getvalue() + def _do_override_item_tag_name(self, stream): + root = ET.fromstring(stream.getvalue()) + for parent in root.findall('.//*list-item/..'): + child_name = parent.tag[0:-1] + for child in parent.getchildren(): + child.tag = child_name + + stream.truncate(0) + stream.write('\n') + stream.write(ET.tostring(root)) + def _to_xml(self, xml, data): if isinstance(data, (list, tuple)): for item in data: From 92f546037f13a7bec628f4dccfca32c5d83a34b8 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 11:13:56 +0200 Subject: [PATCH 05/10] fix for py3 --- tests/test_renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index e627ac4..66aabfe 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -34,7 +34,7 @@ class XMLRendererTestCase(TestCase): } _complex_order_data = { - "creation_date": datetime.datetime(2017, 07, 01, 14, 30, 00), + "creation_date": datetime.datetime(2017, 7, 1, 14, 30, 00), "orderId": 1, "positions": [ { From 441f15c4b2941bc2149a9530499e443183298eb8 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 11:20:41 +0200 Subject: [PATCH 06/10] more fix for py3 (byte vs. str) --- rest_framework_xml/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework_xml/renderers.py b/rest_framework_xml/renderers.py index 1715a8a..6b4b571 100644 --- a/rest_framework_xml/renderers.py +++ b/rest_framework_xml/renderers.py @@ -55,7 +55,7 @@ def _do_override_item_tag_name(self, stream): stream.truncate(0) stream.write('\n') - stream.write(ET.tostring(root)) + stream.write(str(ET.tostring(root))) def _to_xml(self, xml, data): if isinstance(data, (list, tuple)): From a56ce2b9e2df8d2849542b1b4e68b35a4b604b47 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 12:47:05 +0200 Subject: [PATCH 07/10] more py3 compatible --- rest_framework_xml/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework_xml/renderers.py b/rest_framework_xml/renderers.py index 6b4b571..d530dfb 100644 --- a/rest_framework_xml/renderers.py +++ b/rest_framework_xml/renderers.py @@ -5,7 +5,7 @@ from django.utils import six from django.utils.xmlutils import SimplerXMLGenerator -from django.utils.six.moves import StringIO +from django.utils.six import StringIO from django.utils.encoding import force_text from rest_framework.renderers import BaseRenderer from xml.etree import ElementTree as ET From 031142690190e180582729869fa3aaf6a89583d8 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 12:48:22 +0200 Subject: [PATCH 08/10] more py3 compatible --- tests/test_renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 66aabfe..a9637f2 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -6,7 +6,7 @@ from django.test import TestCase from django.test.utils import skipUnless -from django.utils.six.moves import StringIO +from django.utils.six import StringIO from django.utils.translation import gettext_lazy from rest_framework_xml.renderers import XMLRenderer from rest_framework_xml.parsers import XMLParser From f703f4097905d522f7c983be9adb908964560d3b Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Wed, 2 Aug 2017 13:30:01 +0200 Subject: [PATCH 09/10] more py3 compatible --- rest_framework_xml/renderers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest_framework_xml/renderers.py b/rest_framework_xml/renderers.py index d530dfb..2152746 100644 --- a/rest_framework_xml/renderers.py +++ b/rest_framework_xml/renderers.py @@ -50,12 +50,13 @@ def _do_override_item_tag_name(self, stream): root = ET.fromstring(stream.getvalue()) for parent in root.findall('.//*list-item/..'): child_name = parent.tag[0:-1] - for child in parent.getchildren(): + for child in list(parent): child.tag = child_name stream.truncate(0) + stream.seek(0) stream.write('\n') - stream.write(str(ET.tostring(root))) + stream.write(ET.tostring(root).decode('utf-8')) def _to_xml(self, xml, data): if isinstance(data, (list, tuple)): From c3df75831bc96876cff3f3927527b3e7746bfcc3 Mon Sep 17 00:00:00 2001 From: Philipp Allgeuer Date: Fri, 4 Aug 2017 14:40:05 +0200 Subject: [PATCH 10/10] add override_item_tage_name to API documentation --- docs/renderers.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/renderers.md b/docs/renderers.md index 7916053..6f4aec3 100644 --- a/docs/renderers.md +++ b/docs/renderers.md @@ -59,6 +59,8 @@ If you are considering using `XML` for your API, you may want to consider implem **.charset**: `utf-8` -**item_tag_name**: `list-item` +**.item_tag_name**: `list-item` **.root_tag_name**: `root` + +**.override_item_tag_name**: `False`