mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-13 05:06:53 +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.
|
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 django
|
||||||
pip install djangorestframework
|
pip install djangorestframework
|
||||||
|
@ -79,7 +79,7 @@ class UserViewSet(viewsets.ModelViewSet):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
|
||||||
|
|
||||||
# Routers provide a way of automatically determining the URL conf.
|
# Routers provide a way of automatically determining the URL conf.
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register(r'users', UserViewSet)
|
router.register(r'users', UserViewSet)
|
||||||
|
@ -100,7 +100,7 @@ Add the following to your `settings.py` module:
|
||||||
```python
|
```python
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
... # Make sure to include the default installed apps here.
|
... # Make sure to include the default installed apps here.
|
||||||
'rest_framework',
|
'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/
|
$ 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/",
|
"url": "http://127.0.0.1:8000/users/1/",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"email": "admin@example.com",
|
"email": "admin@example.com",
|
||||||
"is_staff": true,
|
"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/
|
$ 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/",
|
"url": "http://127.0.0.1:8000/users/2/",
|
||||||
"username": "new",
|
"username": "new",
|
||||||
"email": "new@example.com",
|
"email": "new@example.com",
|
||||||
"is_staff": false,
|
"is_staff": false,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Documentation & Support
|
# 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
|
Copyright (c) 2011-2014, Tom Christie
|
||||||
All rights reserved.
|
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:
|
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.
|
list of conditions and the following disclaimer.
|
||||||
Redistributions in binary form must reproduce the above copyright notice, this
|
Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
list of conditions and the following disclaimer in the documentation and/or
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
other materials provided with the distribution.
|
other materials provided with the distribution.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
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
|
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.
|
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
|
[urlobject]: https://github.com/zacharyvoase/urlobject
|
||||||
[markdown]: http://pypi.python.org/pypi/Markdown/
|
[markdown]: http://pypi.python.org/pypi/Markdown/
|
||||||
[pyyaml]: http://pypi.python.org/pypi/PyYAML
|
[pyyaml]: http://pypi.python.org/pypi/PyYAML
|
||||||
[defusedxml]: https://pypi.python.org/pypi/defusedxml
|
|
||||||
[django-filter]: http://pypi.python.org/pypi/django-filter
|
[django-filter]: http://pypi.python.org/pypi/django-filter
|
||||||
[security-mail]: mailto:rest-framework-security@googlegroups.com
|
[security-mail]: mailto:rest-framework-security@googlegroups.com
|
||||||
|
|
|
@ -78,18 +78,6 @@ Requires the `pyyaml` package to be installed.
|
||||||
|
|
||||||
**.media_type**: `application/yaml`
|
**.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
|
## FormParser
|
||||||
|
|
||||||
Parses HTML form content. `request.data` will be populated with a `QueryDict` of data.
|
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
|
## 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):
|
class PlainTextParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -145,20 +145,6 @@ Note that non-ascii characters will not be character escaped. For example:
|
||||||
|
|
||||||
**.charset**: `utf-8`
|
**.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
|
## TemplateHTMLRenderer
|
||||||
|
|
||||||
Renders data to HTML, using Django's standard template rendering.
|
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.
|
* [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API.
|
||||||
* [PyYAML][yaml] (3.10+) - YAML content-type support.
|
* [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-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-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.
|
* [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/
|
[eventbrite]: https://www.eventbrite.co.uk/about/
|
||||||
[markdown]: http://pypi.python.org/pypi/Markdown/
|
[markdown]: http://pypi.python.org/pypi/Markdown/
|
||||||
[yaml]: http://pypi.python.org/pypi/PyYAML
|
[yaml]: http://pypi.python.org/pypi/PyYAML
|
||||||
[defusedxml]: https://pypi.python.org/pypi/defusedxml
|
|
||||||
[django-filter]: http://pypi.python.org/pypi/django-filter
|
[django-filter]: http://pypi.python.org/pypi/django-filter
|
||||||
[oauth2]: https://github.com/simplegeo/python-oauth2
|
[oauth2]: https://github.com/simplegeo/python-oauth2
|
||||||
[django-oauth-plus]: https://bitbucket.org/david/django-oauth-plus/wiki/Home
|
[django-oauth-plus]: https://bitbucket.org/david/django-oauth-plus/wiki/Home
|
||||||
|
|
|
@ -7,7 +7,6 @@ flake8==2.2.2
|
||||||
# Optional packages
|
# Optional packages
|
||||||
markdown>=2.1.0
|
markdown>=2.1.0
|
||||||
PyYAML>=3.10
|
PyYAML>=3.10
|
||||||
defusedxml>=0.3
|
|
||||||
django-guardian==1.2.4
|
django-guardian==1.2.4
|
||||||
django-filter>=0.5.4
|
django-filter>=0.5.4
|
||||||
django-oauth-plus>=2.2.1
|
django-oauth-plus>=2.2.1
|
||||||
|
|
|
@ -244,13 +244,6 @@ except ImportError:
|
||||||
yaml = None
|
yaml = None
|
||||||
|
|
||||||
|
|
||||||
# XML is optional
|
|
||||||
try:
|
|
||||||
import defusedxml.ElementTree as etree
|
|
||||||
except ImportError:
|
|
||||||
etree = None
|
|
||||||
|
|
||||||
|
|
||||||
# OAuth2 is optional
|
# OAuth2 is optional
|
||||||
try:
|
try:
|
||||||
# Note: The `oauth2` package actually provides oauth1.0a support. Urg.
|
# 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 MultiPartParser as DjangoMultiPartParser
|
||||||
from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
|
from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
|
||||||
from django.utils import six
|
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.exceptions import ParseError
|
||||||
from rest_framework import renderers
|
from rest_framework import renderers
|
||||||
import json
|
import json
|
||||||
import datetime
|
|
||||||
import decimal
|
|
||||||
|
|
||||||
|
|
||||||
class DataAndFiles(object):
|
class DataAndFiles(object):
|
||||||
|
@ -136,78 +134,6 @@ class MultiPartParser(BaseParser):
|
||||||
raise ParseError('Multipart form parse error - %s' % six.text_type(exc))
|
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):
|
class FileUploadParser(BaseParser):
|
||||||
"""
|
"""
|
||||||
Parser for file upload data.
|
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.template import Context, RequestContext, loader, Template
|
||||||
from django.test.client import encode_multipart
|
from django.test.client import encode_multipart
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.xmlutils import SimplerXMLGenerator
|
|
||||||
from rest_framework import exceptions, serializers, status, VERSION
|
from rest_framework import exceptions, serializers, status, VERSION
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, yaml
|
||||||
SHORT_SEPARATORS, LONG_SEPARATORS, StringIO, smart_text, yaml
|
|
||||||
)
|
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.request import is_form_media_type, override_method
|
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');'
|
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):
|
class YAMLRenderer(BaseRenderer):
|
||||||
"""
|
"""
|
||||||
Renderer which serializes to YAML.
|
Renderer which serializes to YAML.
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from rest_framework.compat import StringIO
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.files.uploadhandler import MemoryFileUploadHandler
|
from django.core.files.uploadhandler import MemoryFileUploadHandler
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import unittest
|
from rest_framework.compat import StringIO
|
||||||
from rest_framework.compat import etree
|
|
||||||
from rest_framework.parsers import FormParser, FileUploadParser
|
from rest_framework.parsers import FormParser, FileUploadParser
|
||||||
from rest_framework.parsers import XMLParser
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
class Form(forms.Form):
|
class Form(forms.Form):
|
||||||
|
@ -31,62 +27,6 @@ class TestFormParser(TestCase):
|
||||||
self.assertEqual(Form(data).is_valid(), True)
|
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):
|
class TestFileUploadParser(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class MockRequest(object):
|
class MockRequest(object):
|
||||||
|
|
|
@ -6,19 +6,18 @@ from django.conf.urls import patterns, url, include
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import TestCase
|
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 django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import status, permissions
|
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.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
|
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
|
||||||
XMLRenderer, JSONPRenderer, BrowsableAPIRenderer
|
JSONPRenderer, BrowsableAPIRenderer
|
||||||
from rest_framework.parsers import YAMLParser, XMLParser
|
from rest_framework.parsers import YAMLParser
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.test import APIRequestFactory
|
from rest_framework.test import APIRequestFactory
|
||||||
from collections import MutableMapping
|
from collections import MutableMapping
|
||||||
import datetime
|
|
||||||
import json
|
import json
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
|
@ -501,104 +500,6 @@ if yaml:
|
||||||
self.assertEqual(content.strip(), 'countries: [United Kingdom, France, España]'.encode('utf-8'))
|
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
|
# Tests for caching issue, #346
|
||||||
class CacheRenderTest(TestCase):
|
class CacheRenderTest(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user