mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +03:00
Remove XML support from core
This commit is contained in:
parent
3a5b3772fe
commit
7f9dc73672
49
README.md
49
README.md
|
@ -53,7 +53,7 @@ Add `'rest_framework'` to your `INSTALLED_APPS` setting.
|
|||
|
||||
Let's take a look at a quick example of using REST framework to build a simple model-backed API for accessing users and groups.
|
||||
|
||||
Startup up a new project like so...
|
||||
Startup up a new project like so...
|
||||
|
||||
pip install django
|
||||
pip install djangorestframework
|
||||
|
@ -79,7 +79,7 @@ class UserViewSet(viewsets.ModelViewSet):
|
|||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
|
||||
|
||||
|
||||
# Routers provide a way of automatically determining the URL conf.
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'users', UserViewSet)
|
||||
|
@ -100,7 +100,7 @@ Add the following to your `settings.py` module:
|
|||
```python
|
||||
INSTALLED_APPS = (
|
||||
... # Make sure to include the default installed apps here.
|
||||
'rest_framework',
|
||||
'rest_framework',
|
||||
)
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
|
@ -123,10 +123,10 @@ You can also interact with the API using command line tools such as [`curl`](htt
|
|||
$ curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/
|
||||
[
|
||||
{
|
||||
"url": "http://127.0.0.1:8000/users/1/",
|
||||
"username": "admin",
|
||||
"email": "admin@example.com",
|
||||
"is_staff": true,
|
||||
"url": "http://127.0.0.1:8000/users/1/",
|
||||
"username": "admin",
|
||||
"email": "admin@example.com",
|
||||
"is_staff": true,
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -134,10 +134,10 @@ Or to create a new user:
|
|||
|
||||
$ curl -X POST -d username=new -d email=new@example.com -d is_staff=false -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/
|
||||
{
|
||||
"url": "http://127.0.0.1:8000/users/2/",
|
||||
"username": "new",
|
||||
"email": "new@example.com",
|
||||
"is_staff": false,
|
||||
"url": "http://127.0.0.1:8000/users/2/",
|
||||
"username": "new",
|
||||
"email": "new@example.com",
|
||||
"is_staff": false,
|
||||
}
|
||||
|
||||
# Documentation & Support
|
||||
|
@ -159,24 +159,24 @@ Send a description of the issue via email to [rest-framework-security@googlegrou
|
|||
Copyright (c) 2011-2014, Tom Christie
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
@ -215,6 +215,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
[urlobject]: https://github.com/zacharyvoase/urlobject
|
||||
[markdown]: http://pypi.python.org/pypi/Markdown/
|
||||
[pyyaml]: http://pypi.python.org/pypi/PyYAML
|
||||
[defusedxml]: https://pypi.python.org/pypi/defusedxml
|
||||
[django-filter]: http://pypi.python.org/pypi/django-filter
|
||||
[security-mail]: mailto:rest-framework-security@googlegroups.com
|
||||
|
|
|
@ -78,18 +78,6 @@ Requires the `pyyaml` package to be installed.
|
|||
|
||||
**.media_type**: `application/yaml`
|
||||
|
||||
## XMLParser
|
||||
|
||||
Parses REST framework's default style of `XML` request content.
|
||||
|
||||
Note that the `XML` markup language is typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
|
||||
|
||||
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
|
||||
|
||||
Requires the `defusedxml` package to be installed.
|
||||
|
||||
**.media_type**: `application/xml`
|
||||
|
||||
## FormParser
|
||||
|
||||
Parses HTML form content. `request.data` will be populated with a `QueryDict` of data.
|
||||
|
@ -161,7 +149,7 @@ By default this will include the following keys: `view`, `request`, `args`, `kwa
|
|||
|
||||
## Example
|
||||
|
||||
The following is an example plaintext parser that will populate the `request.data` property with a string representing the body of the request.
|
||||
The following is an example plaintext parser that will populate the `request.data` property with a string representing the body of the request.
|
||||
|
||||
class PlainTextParser(BaseParser):
|
||||
"""
|
||||
|
|
|
@ -145,20 +145,6 @@ Note that non-ascii characters will not be character escaped. For example:
|
|||
|
||||
**.charset**: `utf-8`
|
||||
|
||||
## XMLRenderer
|
||||
|
||||
Renders REST framework's default style of `XML` response content.
|
||||
|
||||
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
|
||||
|
||||
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
|
||||
|
||||
**.media_type**: `application/xml`
|
||||
|
||||
**.format**: `'.xml'`
|
||||
|
||||
**.charset**: `utf-8`
|
||||
|
||||
## TemplateHTMLRenderer
|
||||
|
||||
Renders data to HTML, using Django's standard template rendering.
|
||||
|
|
|
@ -55,7 +55,6 @@ The following packages are optional:
|
|||
|
||||
* [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API.
|
||||
* [PyYAML][yaml] (3.10+) - YAML content-type support.
|
||||
* [defusedxml][defusedxml] (0.3+) - XML content-type support.
|
||||
* [django-filter][django-filter] (0.5.4+) - Filtering support.
|
||||
* [django-oauth-plus][django-oauth-plus] (2.0+) and [oauth2][oauth2] (1.5.211+) - OAuth 1.0a support.
|
||||
* [django-oauth2-provider][django-oauth2-provider] (0.2.3+) - OAuth 2.0 support.
|
||||
|
@ -259,7 +258,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
[eventbrite]: https://www.eventbrite.co.uk/about/
|
||||
[markdown]: http://pypi.python.org/pypi/Markdown/
|
||||
[yaml]: http://pypi.python.org/pypi/PyYAML
|
||||
[defusedxml]: https://pypi.python.org/pypi/defusedxml
|
||||
[django-filter]: http://pypi.python.org/pypi/django-filter
|
||||
[oauth2]: https://github.com/simplegeo/python-oauth2
|
||||
[django-oauth-plus]: https://bitbucket.org/david/django-oauth-plus/wiki/Home
|
||||
|
|
|
@ -7,7 +7,6 @@ flake8==2.2.2
|
|||
# Optional packages
|
||||
markdown>=2.1.0
|
||||
PyYAML>=3.10
|
||||
defusedxml>=0.3
|
||||
django-guardian==1.2.4
|
||||
django-filter>=0.5.4
|
||||
django-oauth-plus>=2.2.1
|
||||
|
|
|
@ -244,13 +244,6 @@ except ImportError:
|
|||
yaml = None
|
||||
|
||||
|
||||
# XML is optional
|
||||
try:
|
||||
import defusedxml.ElementTree as etree
|
||||
except ImportError:
|
||||
etree = None
|
||||
|
||||
|
||||
# OAuth2 is optional
|
||||
try:
|
||||
# Note: The `oauth2` package actually provides oauth1.0a support. Urg.
|
||||
|
|
|
@ -12,12 +12,10 @@ from django.http import QueryDict
|
|||
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
|
||||
from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
|
||||
from django.utils import six
|
||||
from rest_framework.compat import etree, yaml, force_text, urlparse
|
||||
from rest_framework.compat import yaml, force_text, urlparse
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework import renderers
|
||||
import json
|
||||
import datetime
|
||||
import decimal
|
||||
|
||||
|
||||
class DataAndFiles(object):
|
||||
|
@ -136,78 +134,6 @@ class MultiPartParser(BaseParser):
|
|||
raise ParseError('Multipart form parse error - %s' % six.text_type(exc))
|
||||
|
||||
|
||||
class XMLParser(BaseParser):
|
||||
"""
|
||||
XML parser.
|
||||
"""
|
||||
|
||||
media_type = 'application/xml'
|
||||
|
||||
def parse(self, stream, media_type=None, parser_context=None):
|
||||
"""
|
||||
Parses the incoming bytestream as XML and returns the resulting data.
|
||||
"""
|
||||
assert etree, 'XMLParser requires defusedxml to be installed'
|
||||
|
||||
parser_context = parser_context or {}
|
||||
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
|
||||
parser = etree.DefusedXMLParser(encoding=encoding)
|
||||
try:
|
||||
tree = etree.parse(stream, parser=parser, forbid_dtd=True)
|
||||
except (etree.ParseError, ValueError) as exc:
|
||||
raise ParseError('XML parse error - %s' % six.text_type(exc))
|
||||
data = self._xml_convert(tree.getroot())
|
||||
|
||||
return data
|
||||
|
||||
def _xml_convert(self, element):
|
||||
"""
|
||||
convert the xml `element` into the corresponding python object
|
||||
"""
|
||||
|
||||
children = list(element)
|
||||
|
||||
if len(children) == 0:
|
||||
return self._type_convert(element.text)
|
||||
else:
|
||||
# if the fist child tag is list-item means all children are list-item
|
||||
if children[0].tag == "list-item":
|
||||
data = []
|
||||
for child in children:
|
||||
data.append(self._xml_convert(child))
|
||||
else:
|
||||
data = {}
|
||||
for child in children:
|
||||
data[child.tag] = self._xml_convert(child)
|
||||
|
||||
return data
|
||||
|
||||
def _type_convert(self, value):
|
||||
"""
|
||||
Converts the value returned by the XMl parse into the equivalent
|
||||
Python type
|
||||
"""
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
try:
|
||||
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
return decimal.Decimal(value)
|
||||
except decimal.InvalidOperation:
|
||||
pass
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class FileUploadParser(BaseParser):
|
||||
"""
|
||||
Parser for file upload data.
|
||||
|
|
|
@ -16,11 +16,8 @@ from django.http.multipartparser import parse_header
|
|||
from django.template import Context, RequestContext, loader, Template
|
||||
from django.test.client import encode_multipart
|
||||
from django.utils import six
|
||||
from django.utils.xmlutils import SimplerXMLGenerator
|
||||
from rest_framework import exceptions, serializers, status, VERSION
|
||||
from rest_framework.compat import (
|
||||
SHORT_SEPARATORS, LONG_SEPARATORS, StringIO, smart_text, yaml
|
||||
)
|
||||
from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, yaml
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.settings import api_settings
|
||||
from rest_framework.request import is_form_media_type, override_method
|
||||
|
@ -140,55 +137,6 @@ class JSONPRenderer(JSONRenderer):
|
|||
return callback.encode(self.charset) + b'(' + json + b');'
|
||||
|
||||
|
||||
class XMLRenderer(BaseRenderer):
|
||||
"""
|
||||
Renderer which serializes to XML.
|
||||
"""
|
||||
|
||||
media_type = 'application/xml'
|
||||
format = 'xml'
|
||||
charset = 'utf-8'
|
||||
|
||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||
"""
|
||||
Renders `data` into serialized XML.
|
||||
"""
|
||||
if data is None:
|
||||
return ''
|
||||
|
||||
stream = StringIO()
|
||||
|
||||
xml = SimplerXMLGenerator(stream, self.charset)
|
||||
xml.startDocument()
|
||||
xml.startElement("root", {})
|
||||
|
||||
self._to_xml(xml, data)
|
||||
|
||||
xml.endElement("root")
|
||||
xml.endDocument()
|
||||
return stream.getvalue()
|
||||
|
||||
def _to_xml(self, xml, data):
|
||||
if isinstance(data, (list, tuple)):
|
||||
for item in data:
|
||||
xml.startElement("list-item", {})
|
||||
self._to_xml(xml, item)
|
||||
xml.endElement("list-item")
|
||||
|
||||
elif isinstance(data, dict):
|
||||
for key, value in six.iteritems(data):
|
||||
xml.startElement(key, {})
|
||||
self._to_xml(xml, value)
|
||||
xml.endElement(key)
|
||||
|
||||
elif data is None:
|
||||
# Don't output any value
|
||||
pass
|
||||
|
||||
else:
|
||||
xml.characters(smart_text(data))
|
||||
|
||||
|
||||
class YAMLRenderer(BaseRenderer):
|
||||
"""
|
||||
Renderer which serializes to YAML.
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from rest_framework.compat import StringIO
|
||||
from django import forms
|
||||
from django.core.files.uploadhandler import MemoryFileUploadHandler
|
||||
from django.test import TestCase
|
||||
from django.utils import unittest
|
||||
from rest_framework.compat import etree
|
||||
from rest_framework.compat import StringIO
|
||||
from rest_framework.parsers import FormParser, FileUploadParser
|
||||
from rest_framework.parsers import XMLParser
|
||||
import datetime
|
||||
|
||||
|
||||
class Form(forms.Form):
|
||||
|
@ -31,62 +27,6 @@ class TestFormParser(TestCase):
|
|||
self.assertEqual(Form(data).is_valid(), True)
|
||||
|
||||
|
||||
class TestXMLParser(TestCase):
|
||||
def setUp(self):
|
||||
self._input = StringIO(
|
||||
'<?xml version="1.0" encoding="utf-8"?>'
|
||||
'<root>'
|
||||
'<field_a>121.0</field_a>'
|
||||
'<field_b>dasd</field_b>'
|
||||
'<field_c></field_c>'
|
||||
'<field_d>2011-12-25 12:45:00</field_d>'
|
||||
'</root>'
|
||||
)
|
||||
self._data = {
|
||||
'field_a': 121,
|
||||
'field_b': 'dasd',
|
||||
'field_c': None,
|
||||
'field_d': datetime.datetime(2011, 12, 25, 12, 45, 00)
|
||||
}
|
||||
self._complex_data_input = StringIO(
|
||||
'<?xml version="1.0" encoding="utf-8"?>'
|
||||
'<root>'
|
||||
'<creation_date>2011-12-25 12:45:00</creation_date>'
|
||||
'<sub_data_list>'
|
||||
'<list-item><sub_id>1</sub_id><sub_name>first</sub_name></list-item>'
|
||||
'<list-item><sub_id>2</sub_id><sub_name>second</sub_name></list-item>'
|
||||
'</sub_data_list>'
|
||||
'<name>name</name>'
|
||||
'</root>'
|
||||
)
|
||||
self._complex_data = {
|
||||
"creation_date": datetime.datetime(2011, 12, 25, 12, 45, 00),
|
||||
"name": "name",
|
||||
"sub_data_list": [
|
||||
{
|
||||
"sub_id": 1,
|
||||
"sub_name": "first"
|
||||
},
|
||||
{
|
||||
"sub_id": 2,
|
||||
"sub_name": "second"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@unittest.skipUnless(etree, 'defusedxml not installed')
|
||||
def test_parse(self):
|
||||
parser = XMLParser()
|
||||
data = parser.parse(self._input)
|
||||
self.assertEqual(data, self._data)
|
||||
|
||||
@unittest.skipUnless(etree, 'defusedxml not installed')
|
||||
def test_complex_data_parse(self):
|
||||
parser = XMLParser()
|
||||
data = parser.parse(self._complex_data_input)
|
||||
self.assertEqual(data, self._complex_data)
|
||||
|
||||
|
||||
class TestFileUploadParser(TestCase):
|
||||
def setUp(self):
|
||||
class MockRequest(object):
|
||||
|
|
|
@ -6,19 +6,18 @@ from django.conf.urls import patterns, url, include
|
|||
from django.core.cache import cache
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.utils import six, unittest
|
||||
from django.utils import six
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import status, permissions
|
||||
from rest_framework.compat import yaml, etree, StringIO, BytesIO
|
||||
from rest_framework.compat import yaml, BytesIO
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
|
||||
XMLRenderer, JSONPRenderer, BrowsableAPIRenderer
|
||||
from rest_framework.parsers import YAMLParser, XMLParser
|
||||
JSONPRenderer, BrowsableAPIRenderer
|
||||
from rest_framework.parsers import YAMLParser
|
||||
from rest_framework.settings import api_settings
|
||||
from rest_framework.test import APIRequestFactory
|
||||
from collections import MutableMapping
|
||||
import datetime
|
||||
import json
|
||||
import pickle
|
||||
import re
|
||||
|
@ -501,104 +500,6 @@ if yaml:
|
|||
self.assertEqual(content.strip(), 'countries: [United Kingdom, France, España]'.encode('utf-8'))
|
||||
|
||||
|
||||
class XMLRendererTestCase(TestCase):
|
||||
"""
|
||||
Tests specific to the XML Renderer
|
||||
"""
|
||||
|
||||
_complex_data = {
|
||||
"creation_date": datetime.datetime(2011, 12, 25, 12, 45, 00),
|
||||
"name": "name",
|
||||
"sub_data_list": [
|
||||
{
|
||||
"sub_id": 1,
|
||||
"sub_name": "first"
|
||||
},
|
||||
{
|
||||
"sub_id": 2,
|
||||
"sub_name": "second"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def test_render_string(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render({'field': 'astring'}, 'application/xml')
|
||||
self.assertXMLContains(content, '<field>astring</field>')
|
||||
|
||||
def test_render_integer(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render({'field': 111}, 'application/xml')
|
||||
self.assertXMLContains(content, '<field>111</field>')
|
||||
|
||||
def test_render_datetime(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render({
|
||||
'field': datetime.datetime(2011, 12, 25, 12, 45, 00)
|
||||
}, 'application/xml')
|
||||
self.assertXMLContains(content, '<field>2011-12-25 12:45:00</field>')
|
||||
|
||||
def test_render_float(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render({'field': 123.4}, 'application/xml')
|
||||
self.assertXMLContains(content, '<field>123.4</field>')
|
||||
|
||||
def test_render_decimal(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render({'field': Decimal('111.2')}, 'application/xml')
|
||||
self.assertXMLContains(content, '<field>111.2</field>')
|
||||
|
||||
def test_render_none(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render({'field': None}, 'application/xml')
|
||||
self.assertXMLContains(content, '<field></field>')
|
||||
|
||||
def test_render_complex_data(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = renderer.render(self._complex_data, 'application/xml')
|
||||
self.assertXMLContains(content, '<sub_name>first</sub_name>')
|
||||
self.assertXMLContains(content, '<sub_name>second</sub_name>')
|
||||
|
||||
@unittest.skipUnless(etree, 'defusedxml not installed')
|
||||
def test_render_and_parse_complex_data(self):
|
||||
"""
|
||||
Test XML rendering.
|
||||
"""
|
||||
renderer = XMLRenderer()
|
||||
content = StringIO(renderer.render(self._complex_data, 'application/xml'))
|
||||
|
||||
parser = XMLParser()
|
||||
complex_data_out = parser.parse(content)
|
||||
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('<?xml version="1.0" encoding="utf-8"?>\n<root>'))
|
||||
self.assertTrue(xml.endswith('</root>'))
|
||||
self.assertTrue(string in xml, '%r not in %r' % (string, xml))
|
||||
|
||||
|
||||
# Tests for caching issue, #346
|
||||
class CacheRenderTest(TestCase):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue
Block a user