mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-28 04:24:00 +03:00
Strip trailing whitespace
Result of `find -name '*.py' -exec sed -i 's/[ \t]*$//' {} \;`
This commit is contained in:
parent
4f38e78580
commit
303a1108a7
|
@ -33,13 +33,13 @@ class BaseAuthentication(object):
|
|||
def authenticate(self, request):
|
||||
"""
|
||||
Authenticate the :obj:`request` and return a :obj:`User` or :const:`None`. [*]_
|
||||
|
||||
|
||||
.. [*] The authentication context *will* typically be a :obj:`User`,
|
||||
but it need not be. It can be any user-like object so long as the
|
||||
permissions classes (see the :mod:`permissions` module) on the view can
|
||||
handle the object and use it to determine if the request has the required
|
||||
permissions or not.
|
||||
|
||||
permissions or not.
|
||||
|
||||
This can be an important distinction if you're implementing some token
|
||||
based authentication mechanism, where the authentication context
|
||||
may be more involved than simply mapping to a :obj:`User`.
|
||||
|
@ -55,10 +55,10 @@ class BasicAuthentication(BaseAuthentication):
|
|||
def authenticate(self, request):
|
||||
"""
|
||||
Returns a :obj:`User` if a correct username and password have been supplied
|
||||
using HTTP Basic authentication. Otherwise returns :const:`None`.
|
||||
using HTTP Basic authentication. Otherwise returns :const:`None`.
|
||||
"""
|
||||
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
|
||||
|
||||
|
||||
if 'HTTP_AUTHORIZATION' in request.META:
|
||||
auth = request.META['HTTP_AUTHORIZATION'].split()
|
||||
if len(auth) == 2 and auth[0].lower() == "basic":
|
||||
|
@ -66,17 +66,17 @@ class BasicAuthentication(BaseAuthentication):
|
|||
auth_parts = base64.b64decode(auth[1]).partition(':')
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
uname, passwd = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
|
||||
except DjangoUnicodeDecodeError:
|
||||
return None
|
||||
|
||||
|
||||
user = authenticate(username=uname, password=passwd)
|
||||
if user is not None and user.is_active:
|
||||
return user
|
||||
return None
|
||||
|
||||
|
||||
|
||||
class UserLoggedInAuthentication(BaseAuthentication):
|
||||
"""
|
||||
|
|
|
@ -9,7 +9,7 @@ except ImportError:
|
|||
import StringIO
|
||||
|
||||
|
||||
# parse_qs
|
||||
# parse_qs
|
||||
try:
|
||||
# python >= ?
|
||||
from urlparse import parse_qs
|
||||
|
@ -17,32 +17,32 @@ except ImportError:
|
|||
# python <= ?
|
||||
from cgi import parse_qs
|
||||
|
||||
|
||||
# django.test.client.RequestFactory (Django >= 1.3)
|
||||
|
||||
# django.test.client.RequestFactory (Django >= 1.3)
|
||||
try:
|
||||
from django.test.client import RequestFactory
|
||||
except ImportError:
|
||||
from django.test import Client
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
|
||||
|
||||
# From: http://djangosnippets.org/snippets/963/
|
||||
# Lovely stuff
|
||||
class RequestFactory(Client):
|
||||
"""
|
||||
Class that lets you create mock :obj:`Request` objects for use in testing.
|
||||
|
||||
|
||||
Usage::
|
||||
|
||||
|
||||
rf = RequestFactory()
|
||||
get_request = rf.get('/hello/')
|
||||
post_request = rf.post('/submit/', {'foo': 'bar'})
|
||||
|
||||
|
||||
This class re-uses the :class:`django.test.client.Client` interface. Of which
|
||||
you can find the docs here__.
|
||||
|
||||
|
||||
__ http://www.djangoproject.com/documentation/testing/#the-test-client
|
||||
|
||||
Once you have a `request` object you can pass it to any :func:`view` function,
|
||||
|
||||
Once you have a `request` object you can pass it to any :func:`view` function,
|
||||
just as if that :func:`view` had been hooked up using a URLconf.
|
||||
"""
|
||||
def request(self, **request):
|
||||
|
@ -68,30 +68,30 @@ except ImportError:
|
|||
try:
|
||||
from django.views.generic import View
|
||||
if not hasattr(View, 'head'):
|
||||
# First implementation of Django class-based views did not include head method
|
||||
# First implementation of Django class-based views did not include head method
|
||||
# in base View class - https://code.djangoproject.com/ticket/15668
|
||||
class ViewPlusHead(View):
|
||||
def head(self, request, *args, **kwargs):
|
||||
return self.get(request, *args, **kwargs)
|
||||
View = ViewPlusHead
|
||||
|
||||
|
||||
except ImportError:
|
||||
from django import http
|
||||
from django.utils.functional import update_wrapper
|
||||
# from django.utils.log import getLogger
|
||||
# from django.utils.decorators import classonlymethod
|
||||
|
||||
|
||||
# logger = getLogger('django.request') - We'll just drop support for logger if running Django <= 1.2
|
||||
# Might be nice to fix this up sometime to allow djangorestframework.compat.View to match 1.3's View more closely
|
||||
|
||||
|
||||
class View(object):
|
||||
"""
|
||||
Intentionally simple parent class for all views. Only implements
|
||||
dispatch-by-method and simple sanity checking.
|
||||
"""
|
||||
|
||||
|
||||
http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
|
||||
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Constructor. Called in the URLconf; can contain helpful extra
|
||||
|
@ -101,7 +101,7 @@ except ImportError:
|
|||
# instance, or raise an error.
|
||||
for key, value in kwargs.iteritems():
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
# @classonlymethod - We'll just us classmethod instead if running Django <= 1.2
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
|
@ -117,19 +117,19 @@ except ImportError:
|
|||
if not hasattr(cls, key):
|
||||
raise TypeError(u"%s() received an invalid keyword %r" % (
|
||||
cls.__name__, key))
|
||||
|
||||
|
||||
def view(request, *args, **kwargs):
|
||||
self = cls(**initkwargs)
|
||||
return self.dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
# take name and docstring from class
|
||||
update_wrapper(view, cls, updated=())
|
||||
|
||||
|
||||
# and possible attributes set by decorators
|
||||
# like csrf_exempt from dispatch
|
||||
update_wrapper(view, cls.dispatch, assigned=())
|
||||
return view
|
||||
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# Try to dispatch to the right method; if a method doesn't exist,
|
||||
# defer to the error handler. Also defer to the error handler if the
|
||||
|
@ -142,7 +142,7 @@ except ImportError:
|
|||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
return handler(request, *args, **kwargs)
|
||||
|
||||
|
||||
def http_method_not_allowed(self, request, *args, **kwargs):
|
||||
allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
|
||||
#logger.warning('Method Not Allowed (%s): %s' % (request.method, request.path),
|
||||
|
@ -160,20 +160,20 @@ except ImportError:
|
|||
try:
|
||||
import markdown
|
||||
import re
|
||||
|
||||
|
||||
class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
|
||||
"""
|
||||
Override `markdown`'s :class:`SetextHeaderProcessor`, so that ==== headers are <h2> and ---- headers are <h3>.
|
||||
|
||||
|
||||
We use <h1> for the resource name.
|
||||
"""
|
||||
|
||||
|
||||
# Detect Setext-style header. Must be first 2 lines of block.
|
||||
RE = re.compile(r'^.*?\n[=-]{3,}', re.MULTILINE)
|
||||
|
||||
|
||||
def test(self, parent, block):
|
||||
return bool(self.RE.match(block))
|
||||
|
||||
|
||||
def run(self, parent, blocks):
|
||||
lines = blocks.pop(0).split('\n')
|
||||
# Determine level. ``=`` is 1 and ``-`` is 2.
|
||||
|
@ -186,19 +186,19 @@ try:
|
|||
if len(lines) > 2:
|
||||
# Block contains additional lines. Add to master blocks for later.
|
||||
blocks.insert(0, '\n'.join(lines[2:]))
|
||||
|
||||
|
||||
def apply_markdown(text):
|
||||
"""
|
||||
Simple wrapper around :func:`markdown.markdown` to apply our :class:`CustomSetextHeaderProcessor`,
|
||||
and also set the base level of '#' style headers to <h2>.
|
||||
"""
|
||||
|
||||
|
||||
extensions = ['headerid(level=2)']
|
||||
safe_mode = False,
|
||||
output_format = markdown.DEFAULT_OUTPUT_FORMAT
|
||||
|
||||
md = markdown.Markdown(extensions=markdown.load_extensions(extensions),
|
||||
safe_mode=safe_mode,
|
||||
safe_mode=safe_mode,
|
||||
output_format=output_format)
|
||||
md.parser.blockprocessors['setextheader'] = CustomSetextHeaderProcessor(md.parser)
|
||||
return md.convert(text)
|
||||
|
|
|
@ -49,15 +49,15 @@ class BaseParser(object):
|
|||
in case the parser needs to access any metadata on the :obj:`View` object.
|
||||
"""
|
||||
self.view = view
|
||||
|
||||
|
||||
def can_handle_request(self, content_type):
|
||||
"""
|
||||
Returns :const:`True` if this parser is able to deal with the given *content_type*.
|
||||
|
||||
|
||||
The default implementation for this function is to check the *content_type*
|
||||
argument against the :attr:`media_type` attribute set on the class to see if
|
||||
they match.
|
||||
|
||||
|
||||
This may be overridden to provide for other behavior, but typically you'll
|
||||
instead want to just set the :attr:`media_type` attribute on the class.
|
||||
"""
|
||||
|
@ -97,13 +97,13 @@ if yaml:
|
|||
"""
|
||||
Parses YAML-serialized data.
|
||||
"""
|
||||
|
||||
|
||||
media_type = 'application/yaml'
|
||||
|
||||
|
||||
def parse(self, stream):
|
||||
"""
|
||||
Returns a 2-tuple of `(data, files)`.
|
||||
|
||||
|
||||
`data` will be an object which is the parsed content of the response.
|
||||
`files` will always be `None`.
|
||||
"""
|
||||
|
@ -125,7 +125,7 @@ class PlainTextParser(BaseParser):
|
|||
def parse(self, stream):
|
||||
"""
|
||||
Returns a 2-tuple of `(data, files)`.
|
||||
|
||||
|
||||
`data` will simply be a string representing the body of the request.
|
||||
`files` will always be `None`.
|
||||
"""
|
||||
|
@ -142,7 +142,7 @@ class FormParser(BaseParser):
|
|||
def parse(self, stream):
|
||||
"""
|
||||
Returns a 2-tuple of `(data, files)`.
|
||||
|
||||
|
||||
`data` will be a :class:`QueryDict` containing all the form parameters.
|
||||
`files` will always be :const:`None`.
|
||||
"""
|
||||
|
@ -160,7 +160,7 @@ class MultiPartParser(BaseParser):
|
|||
def parse(self, stream):
|
||||
"""
|
||||
Returns a 2-tuple of `(data, files)`.
|
||||
|
||||
|
||||
`data` will be a :class:`QueryDict` containing all the form parameters.
|
||||
`files` will be a :class:`QueryDict` containing all the form files.
|
||||
"""
|
||||
|
@ -171,8 +171,8 @@ class MultiPartParser(BaseParser):
|
|||
raise ErrorResponse(status.HTTP_400_BAD_REQUEST,
|
||||
{'detail': 'multipart parse error - %s' % unicode(exc)})
|
||||
return django_parser.parse()
|
||||
|
||||
|
||||
|
||||
|
||||
class XMLParser(BaseParser):
|
||||
"""
|
||||
XML parser.
|
||||
|
@ -183,7 +183,7 @@ class XMLParser(BaseParser):
|
|||
def parse(self, stream):
|
||||
"""
|
||||
Returns a 2-tuple of `(data, files)`.
|
||||
|
||||
|
||||
`data` will simply be a string representing the body of the request.
|
||||
`files` will always be `None`.
|
||||
"""
|
||||
|
@ -191,32 +191,32 @@ class XMLParser(BaseParser):
|
|||
tree = ET.parse(stream)
|
||||
for child in tree.getroot().getchildren():
|
||||
data[child.tag] = self._type_convert(child.text)
|
||||
|
||||
|
||||
return (data, None)
|
||||
|
||||
def _type_convert(self, value):
|
||||
|
||||
def _type_convert(self, value):
|
||||
"""
|
||||
Converts the value returned by the XMl parse into the equivalent
|
||||
Converts the value returned by the XMl parse into the equivalent
|
||||
Python type
|
||||
"""
|
||||
if value is None:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ Renderers are used to serialize a View's output into specific media types.
|
|||
|
||||
Django REST framework also provides HTML and PlainText renderers that help self-document the API,
|
||||
by serializing the output along with documentation regarding the View, output status and headers,
|
||||
and providing forms and links depending on the allowed methods, renderers and parsers on the View.
|
||||
and providing forms and links depending on the allowed methods, renderers and parsers on the View.
|
||||
"""
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
|
@ -39,7 +39,7 @@ class BaseRenderer(object):
|
|||
All renderers must extend this class, set the :attr:`media_type` attribute,
|
||||
and override the :meth:`render` method.
|
||||
"""
|
||||
|
||||
|
||||
_FORMAT_QUERY_PARAM = 'format'
|
||||
|
||||
media_type = None
|
||||
|
@ -81,7 +81,7 @@ class BaseRenderer(object):
|
|||
"""
|
||||
if obj is None:
|
||||
return ''
|
||||
|
||||
|
||||
return str(obj)
|
||||
|
||||
|
||||
|
@ -135,10 +135,10 @@ if yaml:
|
|||
"""
|
||||
Renderer which serializes to YAML.
|
||||
"""
|
||||
|
||||
|
||||
media_type = 'application/yaml'
|
||||
format = 'yaml'
|
||||
|
||||
|
||||
def render(self, obj=None, media_type=None):
|
||||
"""
|
||||
Renders *obj* into serialized YAML.
|
||||
|
@ -200,7 +200,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
|
|||
content = renderers[0](view).render(obj, media_type)
|
||||
if not all(char in string.printable for char in content):
|
||||
return '[%d bytes of binary content]'
|
||||
|
||||
|
||||
return content
|
||||
|
||||
|
||||
|
@ -236,7 +236,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
|
|||
# If we still don't have a form instance then try to get an unbound form which can tunnel arbitrary content types
|
||||
if not form_instance:
|
||||
form_instance = self._get_generic_content_form(view)
|
||||
|
||||
|
||||
return form_instance
|
||||
|
||||
|
||||
|
@ -328,7 +328,7 @@ class DocumentingTemplateRenderer(BaseRenderer):
|
|||
'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None),
|
||||
'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX
|
||||
})
|
||||
|
||||
|
||||
ret = template.render(context)
|
||||
|
||||
# Munge DELETE Response code to allow us to return content
|
||||
|
|
|
@ -34,7 +34,7 @@ class BaseResource(Serializer):
|
|||
Typically raises a :exc:`response.ErrorResponse` with status code 400 (Bad Request) on failure.
|
||||
"""
|
||||
return data
|
||||
|
||||
|
||||
def filter_response(self, obj):
|
||||
"""
|
||||
Given the response content, filter it into a serializable object.
|
||||
|
@ -47,11 +47,11 @@ class Resource(BaseResource):
|
|||
A Resource determines how a python object maps to some serializable data.
|
||||
Objects that a resource can act on include plain Python object instances, Django Models, and Django QuerySets.
|
||||
"""
|
||||
|
||||
|
||||
# The model attribute refers to the Django Model which this Resource maps to.
|
||||
# (The Model's class, rather than an instance of the Model)
|
||||
model = None
|
||||
|
||||
|
||||
# By default the set of returned fields will be the set of:
|
||||
#
|
||||
# 0. All the fields on the model, excluding 'id'.
|
||||
|
@ -83,7 +83,7 @@ class FormResource(Resource):
|
|||
"""
|
||||
Given some content as input return some cleaned, validated content.
|
||||
Raises a :exc:`response.ErrorResponse` with status code 400 (Bad Request) on failure.
|
||||
|
||||
|
||||
Validation is standard form validation, with an additional constraint that *no extra unknown fields* may be supplied.
|
||||
|
||||
On failure the :exc:`response.ErrorResponse` content is a dict which may contain :obj:`'errors'` and :obj:`'field-errors'` keys.
|
||||
|
@ -99,27 +99,27 @@ class FormResource(Resource):
|
|||
|
||||
allowed_extra_fields is a list of fields which are not defined by the form, but which we still
|
||||
expect to see on the input.
|
||||
|
||||
|
||||
fake_data is a string that should be used as an extra key, as a kludge to force .errors
|
||||
to be populated when an empty dict is supplied in `data`
|
||||
"""
|
||||
|
||||
|
||||
# We'd like nice error messages even if no content is supplied.
|
||||
# Typically if an empty dict is given to a form Django will
|
||||
# return .is_valid() == False, but .errors == {}
|
||||
#
|
||||
# To get around this case we revalidate with some fake data.
|
||||
# To get around this case we revalidate with some fake data.
|
||||
if fake_data:
|
||||
data[fake_data] = '_fake_data'
|
||||
allowed_extra_fields = tuple(allowed_extra_fields) + ('_fake_data',)
|
||||
|
||||
|
||||
bound_form = self.get_bound_form(data, files)
|
||||
|
||||
if bound_form is None:
|
||||
return data
|
||||
|
||||
|
||||
self.view.bound_form_instance = bound_form
|
||||
|
||||
|
||||
data = data and data or {}
|
||||
files = files and files or {}
|
||||
|
||||
|
@ -130,7 +130,7 @@ class FormResource(Resource):
|
|||
# In addition to regular validation we also ensure no additional fields are being passed in...
|
||||
unknown_fields = seen_fields_set - (form_fields_set | allowed_extra_fields_set)
|
||||
unknown_fields = unknown_fields - set(('csrfmiddlewaretoken', '_accept', '_method')) # TODO: Ugh.
|
||||
|
||||
|
||||
# Check using both regular validation, and our stricter no additional fields rule
|
||||
if bound_form.is_valid() and not unknown_fields:
|
||||
# Validation succeeded...
|
||||
|
@ -155,7 +155,7 @@ class FormResource(Resource):
|
|||
# If we've already set fake_dict and we're still here, fallback gracefully.
|
||||
detail = {u'errors': [u'No content was supplied.']}
|
||||
|
||||
else:
|
||||
else:
|
||||
# Add any non-field errors
|
||||
if bound_form.non_field_errors():
|
||||
detail[u'errors'] = bound_form.non_field_errors()
|
||||
|
@ -171,7 +171,7 @@ class FormResource(Resource):
|
|||
# Add any unknown field errors
|
||||
for key in unknown_fields:
|
||||
field_errors[key] = [u'This field does not exist.']
|
||||
|
||||
|
||||
if field_errors:
|
||||
detail[u'field-errors'] = field_errors
|
||||
|
||||
|
@ -199,7 +199,7 @@ class FormResource(Resource):
|
|||
form = getattr(self.view, '%s_form' % method.lower(), form)
|
||||
|
||||
return form
|
||||
|
||||
|
||||
|
||||
def get_bound_form(self, data=None, files=None, method=None):
|
||||
"""
|
||||
|
@ -237,7 +237,7 @@ class ModelResource(FormResource):
|
|||
Also provides a :meth:`get_bound_form` method which may be used by some renderers.
|
||||
"""
|
||||
|
||||
# Auto-register new ModelResource classes into _model_to_resource
|
||||
# Auto-register new ModelResource classes into _model_to_resource
|
||||
#__metaclass__ = _RegisterModelResource
|
||||
|
||||
form = None
|
||||
|
@ -258,21 +258,21 @@ class ModelResource(FormResource):
|
|||
fields = None
|
||||
"""
|
||||
The list of fields to use on the output.
|
||||
|
||||
|
||||
May be any of:
|
||||
|
||||
|
||||
The name of a model field. To view nested resources, give the field as a tuple of ("fieldName", resource) where `resource` may be any of ModelResource reference, the name of a ModelResourc reference as a string or a tuple of strings representing fields on the nested model.
|
||||
The name of an attribute on the model.
|
||||
The name of an attribute on the resource.
|
||||
The name of a method on the model, with a signature like ``func(self)``.
|
||||
The name of a method on the resource, with a signature like ``func(self, instance)``.
|
||||
"""
|
||||
|
||||
|
||||
exclude = ('id', 'pk')
|
||||
"""
|
||||
The list of fields to exclude. This is only used if :attr:`fields` is not set.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
include = ('url',)
|
||||
"""
|
||||
|
@ -294,7 +294,7 @@ class ModelResource(FormResource):
|
|||
"""
|
||||
Given some content as input return some cleaned, validated content.
|
||||
Raises a :exc:`response.ErrorResponse` with status code 400 (Bad Request) on failure.
|
||||
|
||||
|
||||
Validation is standard form or model form validation,
|
||||
with an additional constraint that no extra unknown fields may be supplied,
|
||||
and that all fields specified by the fields class attribute must be supplied,
|
||||
|
@ -345,12 +345,12 @@ class ModelResource(FormResource):
|
|||
Attempts to reverse resolve the url of the given model *instance* for this resource.
|
||||
|
||||
Requires a ``View`` with :class:`mixins.InstanceMixin` to have been created for this resource.
|
||||
|
||||
|
||||
This method can be overridden if you need to set the resource url reversing explicitly.
|
||||
"""
|
||||
|
||||
if not hasattr(self, 'view_callable'):
|
||||
raise _SkipField
|
||||
raise _SkipField
|
||||
|
||||
# dis does teh magicks...
|
||||
urlconf = get_urlconf()
|
||||
|
@ -393,7 +393,7 @@ class ModelResource(FormResource):
|
|||
return model_fields & set(as_tuple(self.fields))
|
||||
|
||||
return model_fields - set(as_tuple(self.exclude))
|
||||
|
||||
|
||||
@property
|
||||
def _property_fields_set(self):
|
||||
"""
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""
|
||||
The :mod:`response` module provides Response classes you can use in your
|
||||
views to return a certain HTTP response. Typically a response is *rendered*
|
||||
The :mod:`response` module provides Response classes you can use in your
|
||||
views to return a certain HTTP response. Typically a response is *rendered*
|
||||
into a HTTP response depending on what renderers are set on your view and
|
||||
als depending on the accept header of the request.
|
||||
als depending on the accept header of the request.
|
||||
"""
|
||||
|
||||
from django.core.handlers.wsgi import STATUS_CODE_TEXT
|
||||
|
@ -23,7 +23,7 @@ class Response(object):
|
|||
self.raw_content = content # content prior to filtering
|
||||
self.cleaned_content = content # content after filtering
|
||||
self.headers = headers or {}
|
||||
|
||||
|
||||
@property
|
||||
def status_text(self):
|
||||
"""
|
||||
|
|
|
@ -51,10 +51,10 @@ def main():
|
|||
|
||||
# Drop the compat module from coverage, since we're not interested in the coverage
|
||||
# of a module which is specifically for resolving environment dependant imports.
|
||||
# (Because we'll end up getting different coverage reports for it for each environment)
|
||||
# (Because we'll end up getting different coverage reports for it for each environment)
|
||||
if 'compat.py' in files:
|
||||
files.remove('compat.py')
|
||||
|
||||
|
||||
cov_files.extend([os.path.join(path, file) for file in files if file.endswith('.py')])
|
||||
|
||||
cov.report(cov_files)
|
||||
|
|
|
@ -16,12 +16,12 @@ from django.test.utils import get_runner
|
|||
def usage():
|
||||
return """
|
||||
Usage: python runtests.py [UnitTestClass].[method]
|
||||
|
||||
|
||||
You can pass the Class name of the `UnitTestClass` you want to test.
|
||||
|
||||
|
||||
Append a method name if you only want to test a specific method of that class.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
TestRunner = get_runner(settings)
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ MEDIA_URL = ''
|
|||
|
||||
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://foo.com/media/", "/media/".
|
||||
# Examples: "http://foo.com/media/", "/media/".
|
||||
ADMIN_MEDIA_PREFIX = '/media/'
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
|
|
|
@ -18,7 +18,7 @@ _serializers = {}
|
|||
|
||||
def _field_to_tuple(field):
|
||||
"""
|
||||
Convert an item in the `fields` attribute into a 2-tuple.
|
||||
Convert an item in the `fields` attribute into a 2-tuple.
|
||||
"""
|
||||
if isinstance(field, (tuple, list)):
|
||||
return (field[0], field[1])
|
||||
|
@ -52,7 +52,7 @@ class _RegisterSerializer(type):
|
|||
"""
|
||||
def __new__(cls, name, bases, attrs):
|
||||
# Build the class and register it.
|
||||
ret = super(_RegisterSerializer, cls).__new__(cls, name, bases, attrs)
|
||||
ret = super(_RegisterSerializer, cls).__new__(cls, name, bases, attrs)
|
||||
_serializers[name] = ret
|
||||
return ret
|
||||
|
||||
|
@ -61,19 +61,19 @@ class Serializer(object):
|
|||
"""
|
||||
Converts python objects into plain old native types suitable for
|
||||
serialization. In particular it handles models and querysets.
|
||||
|
||||
|
||||
The output format is specified by setting a number of attributes
|
||||
on the class.
|
||||
|
||||
You may also override any of the serialization methods, to provide
|
||||
for more flexible behavior.
|
||||
|
||||
|
||||
Valid output types include anything that may be directly rendered into
|
||||
json, xml etc...
|
||||
"""
|
||||
__metaclass__ = _RegisterSerializer
|
||||
|
||||
fields = ()
|
||||
fields = ()
|
||||
"""
|
||||
Specify the fields to be serialized on a model or dict.
|
||||
Overrides `include` and `exclude`.
|
||||
|
@ -109,7 +109,7 @@ class Serializer(object):
|
|||
if depth is not None:
|
||||
self.depth = depth
|
||||
self.stack = stack
|
||||
|
||||
|
||||
|
||||
def get_fields(self, obj):
|
||||
"""
|
||||
|
@ -168,7 +168,7 @@ class Serializer(object):
|
|||
# Similar to what Django does for cyclically related models.
|
||||
elif isinstance(info, str) and info in _serializers:
|
||||
return _serializers[info]
|
||||
|
||||
|
||||
# Otherwise use `related_serializer` or fall back to `Serializer`
|
||||
return getattr(self, 'related_serializer') or Serializer
|
||||
|
||||
|
@ -186,7 +186,7 @@ class Serializer(object):
|
|||
Convert a model field or dict value into a serializable representation.
|
||||
"""
|
||||
related_serializer = self.get_related_serializer(key)
|
||||
|
||||
|
||||
if self.depth is None:
|
||||
depth = None
|
||||
elif self.depth <= 0:
|
||||
|
@ -227,7 +227,7 @@ class Serializer(object):
|
|||
|
||||
fields = self.get_fields(instance)
|
||||
|
||||
# serialize each required field
|
||||
# serialize each required field
|
||||
for fname in fields:
|
||||
try:
|
||||
if hasattr(self, smart_str(fname)):
|
||||
|
@ -279,13 +279,13 @@ class Serializer(object):
|
|||
Convert any unhandled object into a serializable representation.
|
||||
"""
|
||||
return smart_unicode(obj, strings_only=True)
|
||||
|
||||
|
||||
|
||||
|
||||
def serialize(self, obj):
|
||||
"""
|
||||
Convert any object into a serializable representation.
|
||||
"""
|
||||
|
||||
|
||||
if isinstance(obj, (dict, models.Model)):
|
||||
# Model instances & dictionaries
|
||||
return self.serialize_model(obj)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Adds the custom filter 'urlize_quoted_links'
|
||||
|
||||
This is identical to the built-in filter 'urlize' with the exception that
|
||||
This is identical to the built-in filter 'urlize' with the exception that
|
||||
single and double quotes are permitted as leading or trailing punctuation.
|
||||
"""
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class UserAgentMungingTest(TestCase):
|
|||
req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
|
||||
resp = view(req)
|
||||
self.assertEqual(resp['Content-Type'], 'application/json')
|
||||
|
||||
|
||||
def test_dont_munge_nice_browsers_accept_header(self):
|
||||
"""Send Non-MSIE user agent strings and ensure that we get a JSON response,
|
||||
if we set a */* Accept header. (Other browsers will correctly set the Accept header)"""
|
||||
|
|
|
@ -31,7 +31,7 @@ class BasicAuthTests(TestCase):
|
|||
self.username = 'john'
|
||||
self.email = 'lennon@thebeatles.com'
|
||||
self.password = 'password'
|
||||
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||
|
||||
def test_post_form_passing_basic_auth(self):
|
||||
"""Ensure POSTing json over basic auth with correct credentials passes and does not require CSRF"""
|
||||
|
@ -66,7 +66,7 @@ class SessionAuthTests(TestCase):
|
|||
self.username = 'john'
|
||||
self.email = 'lennon@thebeatles.com'
|
||||
self.password = 'password'
|
||||
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||
|
||||
def tearDown(self):
|
||||
self.csrf_client.logout()
|
||||
|
|
|
@ -55,16 +55,16 @@ class TestViewNamesAndDescriptions(TestCase):
|
|||
|
||||
* list
|
||||
* list
|
||||
|
||||
|
||||
another header
|
||||
--------------
|
||||
|
||||
code block
|
||||
|
||||
indented
|
||||
|
||||
|
||||
# hash style header #"""
|
||||
|
||||
|
||||
self.assertEquals(get_description(MockView()), DESCRIPTION)
|
||||
|
||||
# This has been turned off now
|
||||
|
@ -75,7 +75,7 @@ class TestViewNamesAndDescriptions(TestCase):
|
|||
# """docstring"""
|
||||
# description = example
|
||||
# self.assertEquals(get_description(MockView()), example)
|
||||
|
||||
|
||||
#def test_resource_description_does_not_require_docstring(self):
|
||||
# """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'description' class attribute."""
|
||||
# example = 'Some other description'
|
||||
|
@ -88,7 +88,7 @@ class TestViewNamesAndDescriptions(TestCase):
|
|||
class MockView(View):
|
||||
pass
|
||||
self.assertEquals(get_description(MockView()), '')
|
||||
|
||||
|
||||
def test_markdown(self):
|
||||
"""Ensure markdown to HTML works as expected"""
|
||||
if apply_markdown:
|
||||
|
|
|
@ -22,7 +22,7 @@ class UploadFilesTests(TestCase):
|
|||
def post(self, request, *args, **kwargs):
|
||||
return {'FILE_NAME': self.CONTENT['file'].name,
|
||||
'FILE_CONTENT': self.CONTENT['file'].read()}
|
||||
|
||||
|
||||
file = StringIO.StringIO('stuff')
|
||||
file.name = 'stuff.txt'
|
||||
request = self.factory.post('/', {'file': file})
|
||||
|
|
|
@ -3,7 +3,7 @@ from djangorestframework.compat import RequestFactory
|
|||
from djangorestframework.mixins import RequestMixin
|
||||
|
||||
|
||||
class TestMethodOverloading(TestCase):
|
||||
class TestMethodOverloading(TestCase):
|
||||
def setUp(self):
|
||||
self.req = RequestFactory()
|
||||
|
||||
|
@ -18,7 +18,7 @@ class TestMethodOverloading(TestCase):
|
|||
view = RequestMixin()
|
||||
view.request = self.req.post('/')
|
||||
self.assertEqual(view.method, 'POST')
|
||||
|
||||
|
||||
def test_overloaded_POST_behaviour_determines_overloaded_method(self):
|
||||
"""POST requests can be overloaded to another method by setting a reserved form field"""
|
||||
view = RequestMixin()
|
||||
|
|
|
@ -25,4 +25,4 @@ class UserGroupMap(models.Model):
|
|||
def get_absolute_url(self):
|
||||
return ('user_group_map', (), {
|
||||
'pk': self.id
|
||||
})
|
||||
})
|
|
@ -17,9 +17,9 @@ class UserForm(ModelForm):
|
|||
class UserResource(ModelResource):
|
||||
model = User
|
||||
form = UserForm
|
||||
|
||||
|
||||
class CustomUserResource(ModelResource):
|
||||
model = CustomUser
|
||||
model = CustomUser
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^users/$', ListOrCreateModelView.as_view(resource=UserResource), name='users'),
|
||||
|
@ -33,7 +33,7 @@ urlpatterns = patterns('',
|
|||
|
||||
class ModelViewTests(TestCase):
|
||||
"""Test the model views djangorestframework provides"""
|
||||
urls = 'djangorestframework.tests.modelviews'
|
||||
urls = 'djangorestframework.tests.modelviews'
|
||||
|
||||
def test_creation(self):
|
||||
"""Ensure that a model object can be created"""
|
||||
|
@ -52,18 +52,18 @@ class ModelViewTests(TestCase):
|
|||
self.assertEqual(0, User.objects.count())
|
||||
|
||||
response = self.client.post('/users/', {'username': 'bar', 'password': 'baz', 'groups': [group.id]})
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(1, User.objects.count())
|
||||
|
||||
|
||||
user = User.objects.all()[0]
|
||||
self.assertEqual('bar', user.username)
|
||||
self.assertEqual('baz', user.password)
|
||||
self.assertEqual(1, user.groups.count())
|
||||
|
||||
|
||||
group = user.groups.all()[0]
|
||||
self.assertEqual('foo', group.name)
|
||||
|
||||
|
||||
def test_creation_with_m2m_relation_through(self):
|
||||
"""
|
||||
Ensure that a model object with a m2m relation can be created where that
|
||||
|
@ -74,13 +74,13 @@ class ModelViewTests(TestCase):
|
|||
self.assertEqual(0, User.objects.count())
|
||||
|
||||
response = self.client.post('/customusers/', {'username': 'bar', 'groups': [group.id]})
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(1, CustomUser.objects.count())
|
||||
|
||||
|
||||
user = CustomUser.objects.all()[0]
|
||||
self.assertEqual('bar', user.username)
|
||||
self.assertEqual(1, user.groups.count())
|
||||
|
||||
|
||||
group = user.groups.all()[0]
|
||||
self.assertEqual('foo', group.name)
|
||||
self.assertEqual('foo', group.name)
|
||||
|
|
|
@ -23,14 +23,14 @@ else:
|
|||
class ClientView(View):
|
||||
def get(self, request):
|
||||
return {'resource': 'Protected!'}
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', oauth_required(ClientView.as_view())),
|
||||
url(r'^oauth/', include('oauth_provider.urls')),
|
||||
url(r'^accounts/login/$', 'djangorestframework.utils.staticviews.api_login'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class OAuthTests(TestCase):
|
||||
"""
|
||||
OAuth authentication:
|
||||
|
@ -42,23 +42,23 @@ else:
|
|||
* the third-party website is able to retrieve data from the API
|
||||
"""
|
||||
urls = 'djangorestframework.tests.oauthentication'
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.username = 'john'
|
||||
self.email = 'lennon@thebeatles.com'
|
||||
self.password = 'password'
|
||||
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||
|
||||
|
||||
# OAuth requirements
|
||||
self.resource = Resource(name='data', url='/')
|
||||
self.resource.save()
|
||||
self.CONSUMER_KEY = 'dpf43f3p2l4k3l03'
|
||||
self.CONSUMER_SECRET = 'kd94hf93k423kf44'
|
||||
self.consumer = Consumer(key=self.CONSUMER_KEY, secret=self.CONSUMER_SECRET,
|
||||
self.consumer = Consumer(key=self.CONSUMER_KEY, secret=self.CONSUMER_SECRET,
|
||||
name='api.example.com', user=self.user)
|
||||
self.consumer.save()
|
||||
|
||||
|
||||
def test_oauth_invalid_and_anonymous_access(self):
|
||||
"""
|
||||
Verify that the resource is protected and the OAuth authorization view
|
||||
|
@ -69,16 +69,16 @@ else:
|
|||
self.assertEqual(response.status_code, 401)
|
||||
response = self.client.get('/oauth/authorize/', follow=True)
|
||||
self.assertRedirects(response, '/accounts/login/?next=/oauth/authorize/')
|
||||
|
||||
|
||||
def test_oauth_authorize_access(self):
|
||||
"""
|
||||
Verify that once logged in, the user can access the authorization page
|
||||
Verify that once logged in, the user can access the authorization page
|
||||
but can't display the page because the request token is not specified.
|
||||
"""
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
response = self.client.get('/oauth/authorize/', follow=True)
|
||||
self.assertEqual(response.content, 'No request token specified.')
|
||||
|
||||
|
||||
def _create_request_token_parameters(self):
|
||||
"""
|
||||
A shortcut to create request's token parameters.
|
||||
|
@ -93,28 +93,28 @@ else:
|
|||
'oauth_callback': 'http://api.example.com/request_token_ready',
|
||||
'scope': 'data',
|
||||
}
|
||||
|
||||
|
||||
def test_oauth_request_token_retrieval(self):
|
||||
"""
|
||||
Verify that the request token can be retrieved by the server.
|
||||
"""
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
self._create_request_token_parameters())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
token = list(Token.objects.all())[-1]
|
||||
self.failIf(token.key not in response.content)
|
||||
self.failIf(token.secret not in response.content)
|
||||
|
||||
|
||||
def test_oauth_user_request_authorization(self):
|
||||
"""
|
||||
Verify that the user can access the authorization page once logged in
|
||||
and the request token has been retrieved.
|
||||
"""
|
||||
# Setup
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
self._create_request_token_parameters())
|
||||
token = list(Token.objects.all())[-1]
|
||||
|
||||
|
||||
# Starting the test here
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
parameters = {'oauth_token': token.key,}
|
||||
|
@ -129,7 +129,7 @@ else:
|
|||
token = Token.objects.get(key=token.key)
|
||||
self.failIf(token.key not in response['Location'])
|
||||
self.assertEqual(token.is_approved, 1)
|
||||
|
||||
|
||||
def _create_access_token_parameters(self, token):
|
||||
"""
|
||||
A shortcut to create access' token parameters.
|
||||
|
@ -145,13 +145,13 @@ else:
|
|||
'oauth_verifier': token.verifier,
|
||||
'scope': 'data',
|
||||
}
|
||||
|
||||
|
||||
def test_oauth_access_token_retrieval(self):
|
||||
"""
|
||||
Verify that the request token can be retrieved by the server.
|
||||
"""
|
||||
# Setup
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
self._create_request_token_parameters())
|
||||
token = list(Token.objects.all())[-1]
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
|
@ -160,7 +160,7 @@ else:
|
|||
parameters['authorize_access'] = 1 # fake authorization by the user
|
||||
response = self.client.post("/oauth/authorize/", parameters)
|
||||
token = Token.objects.get(key=token.key)
|
||||
|
||||
|
||||
# Starting the test here
|
||||
response = self.client.get("/oauth/access_token/", self._create_access_token_parameters(token))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -169,7 +169,7 @@ else:
|
|||
self.failIf(access_token.key not in response.content)
|
||||
self.failIf(access_token.secret not in response.content)
|
||||
self.assertEqual(access_token.user.username, 'john')
|
||||
|
||||
|
||||
def _create_access_parameters(self, access_token):
|
||||
"""
|
||||
A shortcut to create access' parameters.
|
||||
|
@ -188,13 +188,13 @@ else:
|
|||
signature = signature_method.sign(oauth_request, self.consumer, access_token)
|
||||
parameters['oauth_signature'] = signature
|
||||
return parameters
|
||||
|
||||
|
||||
def test_oauth_protected_resource_access(self):
|
||||
"""
|
||||
Verify that the request token can be retrieved by the server.
|
||||
"""
|
||||
# Setup
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
response = self.client.get("/oauth/request_token/",
|
||||
self._create_request_token_parameters())
|
||||
token = list(Token.objects.all())[-1]
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
|
@ -205,7 +205,7 @@ else:
|
|||
token = Token.objects.get(key=token.key)
|
||||
response = self.client.get("/oauth/access_token/", self._create_access_token_parameters(token))
|
||||
access_token = list(Token.objects.filter(token_type=Token.ACCESS))[-1]
|
||||
|
||||
|
||||
# Starting the test here
|
||||
response = self.client.get("/", self._create_access_token_parameters(access_token))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from django.test import TestCase
|
||||
import djangorestframework
|
||||
|
||||
class TestVersion(TestCase):
|
||||
class TestVersion(TestCase):
|
||||
"""Simple sanity test to check the VERSION exists"""
|
||||
|
||||
def test_version(self):
|
||||
|
|
|
@ -8,76 +8,76 @@
|
|||
# >>> req = RequestFactory().get('/')
|
||||
# >>> some_view = View()
|
||||
# >>> some_view.request = req # Make as if this request had been dispatched
|
||||
#
|
||||
#
|
||||
# FormParser
|
||||
# ============
|
||||
#
|
||||
#
|
||||
# Data flatening
|
||||
# ----------------
|
||||
#
|
||||
#
|
||||
# Here is some example data, which would eventually be sent along with a post request :
|
||||
#
|
||||
#
|
||||
# >>> inpt = urlencode([
|
||||
# ... ('key1', 'bla1'),
|
||||
# ... ('key2', 'blo1'), ('key2', 'blo2'),
|
||||
# ... ])
|
||||
#
|
||||
#
|
||||
# Default behaviour for :class:`parsers.FormParser`, is to return a single value for each parameter :
|
||||
#
|
||||
#
|
||||
# >>> (data, files) = FormParser(some_view).parse(StringIO(inpt))
|
||||
# >>> data == {'key1': 'bla1', 'key2': 'blo1'}
|
||||
# True
|
||||
#
|
||||
#
|
||||
# However, you can customize this behaviour by subclassing :class:`parsers.FormParser`, and overriding :meth:`parsers.FormParser.is_a_list` :
|
||||
#
|
||||
#
|
||||
# >>> class MyFormParser(FormParser):
|
||||
# ...
|
||||
# ...
|
||||
# ... def is_a_list(self, key, val_list):
|
||||
# ... return len(val_list) > 1
|
||||
#
|
||||
#
|
||||
# This new parser only flattens the lists of parameters that contain a single value.
|
||||
#
|
||||
#
|
||||
# >>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
|
||||
# >>> data == {'key1': 'bla1', 'key2': ['blo1', 'blo2']}
|
||||
# True
|
||||
#
|
||||
#
|
||||
# .. note:: The same functionality is available for :class:`parsers.MultiPartParser`.
|
||||
#
|
||||
#
|
||||
# Submitting an empty list
|
||||
# --------------------------
|
||||
#
|
||||
#
|
||||
# When submitting an empty select multiple, like this one ::
|
||||
#
|
||||
#
|
||||
# <select multiple="multiple" name="key2"></select>
|
||||
#
|
||||
#
|
||||
# The browsers usually strip the parameter completely. A hack to avoid this, and therefore being able to submit an empty select multiple, is to submit a value that tells the server that the list is empty ::
|
||||
#
|
||||
#
|
||||
# <select multiple="multiple" name="key2"><option value="_empty"></select>
|
||||
#
|
||||
#
|
||||
# :class:`parsers.FormParser` provides the server-side implementation for this hack. Considering the following posted data :
|
||||
#
|
||||
#
|
||||
# >>> inpt = urlencode([
|
||||
# ... ('key1', 'blo1'), ('key1', '_empty'),
|
||||
# ... ('key2', '_empty'),
|
||||
# ... ])
|
||||
#
|
||||
#
|
||||
# :class:`parsers.FormParser` strips the values ``_empty`` from all the lists.
|
||||
#
|
||||
#
|
||||
# >>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
|
||||
# >>> data == {'key1': 'blo1'}
|
||||
# True
|
||||
#
|
||||
#
|
||||
# Oh ... but wait a second, the parameter ``key2`` isn't even supposed to be a list, so the parser just stripped it.
|
||||
#
|
||||
#
|
||||
# >>> class MyFormParser(FormParser):
|
||||
# ...
|
||||
# ...
|
||||
# ... def is_a_list(self, key, val_list):
|
||||
# ... return key == 'key2'
|
||||
# ...
|
||||
# ...
|
||||
# >>> (data, files) = MyFormParser(some_view).parse(StringIO(inpt))
|
||||
# >>> data == {'key1': 'blo1', 'key2': []}
|
||||
# True
|
||||
#
|
||||
#
|
||||
# Better like that. Note that you can configure something else than ``_empty`` for the empty value by setting :attr:`parsers.FormParser.EMPTY_VALUE`.
|
||||
# """
|
||||
# import httplib, mimetypes
|
||||
|
@ -87,7 +87,7 @@
|
|||
# from djangorestframework.parsers import MultiPartParser
|
||||
# from djangorestframework.views import View
|
||||
# from StringIO import StringIO
|
||||
#
|
||||
#
|
||||
# def encode_multipart_formdata(fields, files):
|
||||
# """For testing multipart parser.
|
||||
# fields is a sequence of (name, value) elements for regular form fields.
|
||||
|
@ -112,10 +112,10 @@
|
|||
# body = CRLF.join(L)
|
||||
# content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
|
||||
# return content_type, body
|
||||
#
|
||||
#
|
||||
# def get_content_type(filename):
|
||||
# return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
||||
#
|
||||
#
|
||||
#class TestMultiPartParser(TestCase):
|
||||
# def setUp(self):
|
||||
# self.req = RequestFactory()
|
||||
|
@ -145,18 +145,18 @@ class Form(forms.Form):
|
|||
|
||||
class TestFormParser(TestCase):
|
||||
def setUp(self):
|
||||
self.string = "field1=abc&field2=defghijk"
|
||||
|
||||
self.string = "field1=abc&field2=defghijk"
|
||||
|
||||
def test_parse(self):
|
||||
""" Make sure the `QueryDict` works OK """
|
||||
parser = FormParser(None)
|
||||
|
||||
|
||||
stream = StringIO(self.string)
|
||||
(data, files) = parser.parse(stream)
|
||||
|
||||
self.assertEqual(Form(data).is_valid(), True)
|
||||
|
||||
|
||||
|
||||
class TestXMLParser(TestCase):
|
||||
def setUp(self):
|
||||
self.input = StringIO(
|
||||
|
@ -164,18 +164,18 @@ class TestXMLParser(TestCase):
|
|||
'<root>'
|
||||
'<field_a>121.0</field_a>'
|
||||
'<field_b>dasd</field_b>'
|
||||
'<field_c></field_c>'
|
||||
'<field_c></field_c>'
|
||||
'<field_d>2011-12-25 12:45:00</field_d>'
|
||||
'</root>'
|
||||
)
|
||||
self.data = {
|
||||
)
|
||||
self.data = {
|
||||
'field_a': 121,
|
||||
'field_b': 'dasd',
|
||||
'field_b': 'dasd',
|
||||
'field_c': None,
|
||||
'field_d': datetime.datetime(2011, 12, 25, 12, 45, 00)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
def test_parse(self):
|
||||
parser = XMLParser(None)
|
||||
(data, files) = parser.parse(self.input)
|
||||
|
|
|
@ -41,7 +41,7 @@ class MockView(ResponseMixin, DjangoView):
|
|||
def get(self, request, **kwargs):
|
||||
response = Response(DUMMYSTATUS, DUMMYCONTENT)
|
||||
return self.render(response)
|
||||
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderers=[RendererA, RendererB])),
|
||||
|
@ -92,7 +92,7 @@ class RendererIntegrationTests(TestCase):
|
|||
self.assertEquals(resp['Content-Type'], RendererB.media_type)
|
||||
self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
|
||||
self.assertEquals(resp.status_code, DUMMYSTATUS)
|
||||
|
||||
|
||||
def test_specified_renderer_serializes_content_on_accept_query(self):
|
||||
"""The '_accept' query string should behave in the same way as the Accept header."""
|
||||
resp = self.client.get('/?_accept=%s' % RendererB.media_type)
|
||||
|
@ -150,7 +150,7 @@ _flat_repr = '{"foo": ["bar", "baz"]}'
|
|||
|
||||
_indented_repr = """{
|
||||
"foo": [
|
||||
"bar",
|
||||
"bar",
|
||||
"baz"
|
||||
]
|
||||
}"""
|
||||
|
@ -172,13 +172,13 @@ class JSONRendererTests(TestCase):
|
|||
|
||||
def test_with_content_type_args(self):
|
||||
"""
|
||||
Test JSON rendering with additional content type arguments supplied.
|
||||
Test JSON rendering with additional content type arguments supplied.
|
||||
"""
|
||||
obj = {'foo':['bar','baz']}
|
||||
renderer = JSONRenderer(None)
|
||||
content = renderer.render(obj, 'application/json; indent=2')
|
||||
self.assertEquals(content, _indented_repr)
|
||||
|
||||
|
||||
def test_render_and_parse(self):
|
||||
"""
|
||||
Test rendering and then parsing returns the original object.
|
||||
|
@ -191,19 +191,19 @@ class JSONRendererTests(TestCase):
|
|||
|
||||
content = renderer.render(obj, 'application/json')
|
||||
(data, files) = parser.parse(StringIO(content))
|
||||
self.assertEquals(obj, data)
|
||||
self.assertEquals(obj, data)
|
||||
|
||||
|
||||
|
||||
if YAMLRenderer:
|
||||
_yaml_repr = 'foo: [bar, baz]\n'
|
||||
|
||||
|
||||
|
||||
|
||||
class YAMLRendererTests(TestCase):
|
||||
"""
|
||||
Tests specific to the JSON Renderer
|
||||
"""
|
||||
|
||||
|
||||
def test_render(self):
|
||||
"""
|
||||
Test basic YAML rendering.
|
||||
|
@ -212,24 +212,24 @@ if YAMLRenderer:
|
|||
renderer = YAMLRenderer(None)
|
||||
content = renderer.render(obj, 'application/yaml')
|
||||
self.assertEquals(content, _yaml_repr)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_render_and_parse(self):
|
||||
"""
|
||||
Test rendering and then parsing returns the original object.
|
||||
IE obj -> render -> parse -> obj.
|
||||
"""
|
||||
obj = {'foo':['bar','baz']}
|
||||
|
||||
|
||||
renderer = YAMLRenderer(None)
|
||||
parser = YAMLParser(None)
|
||||
|
||||
|
||||
content = renderer.render(obj, 'application/yaml')
|
||||
(data, files) = parser.parse(StringIO(content))
|
||||
self.assertEquals(obj, data)
|
||||
self.assertEquals(obj, data)
|
||||
|
||||
|
||||
|
||||
|
||||
class XMLRendererTestCase(TestCase):
|
||||
"""
|
||||
Tests specific to the XML Renderer
|
||||
|
@ -289,4 +289,4 @@ class XMLRendererTestCase(TestCase):
|
|||
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))
|
||||
self.assertTrue(string in xml, '%r not in %r' % (string, xml))
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#from djangorestframework.response import Response
|
||||
#
|
||||
#
|
||||
#class TestResponse(TestCase):
|
||||
#class TestResponse(TestCase):
|
||||
#
|
||||
# # Interface tests
|
||||
#
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.db import models
|
|||
import datetime
|
||||
import decimal
|
||||
|
||||
class TestObjectToData(TestCase):
|
||||
class TestObjectToData(TestCase):
|
||||
"""
|
||||
Tests for the Serializer class.
|
||||
"""
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.test import TestCase
|
|||
from djangorestframework import status
|
||||
|
||||
|
||||
class TestStatus(TestCase):
|
||||
class TestStatus(TestCase):
|
||||
"""Simple sanity test to check the status module"""
|
||||
|
||||
def test_status(self):
|
||||
|
|
|
@ -21,25 +21,25 @@ class MockView(View):
|
|||
class MockView_PerViewThrottling(MockView):
|
||||
permissions = ( PerViewThrottling, )
|
||||
|
||||
class MockView_PerResourceThrottling(MockView):
|
||||
class MockView_PerResourceThrottling(MockView):
|
||||
permissions = ( PerResourceThrottling, )
|
||||
resource = FormResource
|
||||
|
||||
class MockView_MinuteThrottling(MockView):
|
||||
throttle = '3/min'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ThrottlingTests(TestCase):
|
||||
urls = 'djangorestframework.tests.throttling'
|
||||
|
||||
urls = 'djangorestframework.tests.throttling'
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Reset the cache so that no throttles will be active
|
||||
"""
|
||||
cache.clear()
|
||||
self.factory = RequestFactory()
|
||||
|
||||
|
||||
def test_requests_are_throttled(self):
|
||||
"""
|
||||
Ensure request rate is limited
|
||||
|
@ -48,7 +48,7 @@ class ThrottlingTests(TestCase):
|
|||
for dummy in range(4):
|
||||
response = MockView.as_view()(request)
|
||||
self.assertEqual(503, response.status_code)
|
||||
|
||||
|
||||
def set_throttle_timer(self, view, value):
|
||||
"""
|
||||
Explicitly set the timer, overriding time.time()
|
||||
|
@ -71,7 +71,7 @@ class ThrottlingTests(TestCase):
|
|||
|
||||
response = MockView.as_view()(request)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
|
||||
def ensure_is_throttled(self, view, expect):
|
||||
request = self.factory.get('/')
|
||||
request.user = User.objects.create(username='a')
|
||||
|
@ -80,27 +80,27 @@ class ThrottlingTests(TestCase):
|
|||
request.user = User.objects.create(username='b')
|
||||
response = view.as_view()(request)
|
||||
self.assertEqual(expect, response.status_code)
|
||||
|
||||
|
||||
def test_request_throttling_is_per_user(self):
|
||||
"""
|
||||
Ensure request rate is only limited per user, not globally for
|
||||
Ensure request rate is only limited per user, not globally for
|
||||
PerUserThrottles
|
||||
"""
|
||||
self.ensure_is_throttled(MockView, 200)
|
||||
|
||||
|
||||
def test_request_throttling_is_per_view(self):
|
||||
"""
|
||||
Ensure request rate is limited globally per View for PerViewThrottles
|
||||
"""
|
||||
self.ensure_is_throttled(MockView_PerViewThrottling, 503)
|
||||
|
||||
|
||||
def test_request_throttling_is_per_resource(self):
|
||||
"""
|
||||
Ensure request rate is limited globally per Resource for PerResourceThrottles
|
||||
"""
|
||||
"""
|
||||
self.ensure_is_throttled(MockView_PerResourceThrottling, 503)
|
||||
|
||||
|
||||
|
||||
|
||||
def ensure_response_header_contains_proper_throttle_field(self, view, expected_headers):
|
||||
"""
|
||||
Ensure the response returns an X-Throttle field with status and next attributes
|
||||
|
@ -111,7 +111,7 @@ class ThrottlingTests(TestCase):
|
|||
self.set_throttle_timer(view, timer)
|
||||
response = view.as_view()(request)
|
||||
self.assertEquals(response['X-Throttle'], expect)
|
||||
|
||||
|
||||
def test_seconds_fields(self):
|
||||
"""
|
||||
Ensure for second based throttles.
|
||||
|
@ -122,7 +122,7 @@ class ThrottlingTests(TestCase):
|
|||
(0, 'status=SUCCESS; next=1.00 sec'),
|
||||
(0, 'status=FAILURE; next=1.00 sec')
|
||||
))
|
||||
|
||||
|
||||
def test_minutes_fields(self):
|
||||
"""
|
||||
Ensure for minute based throttles.
|
||||
|
@ -133,7 +133,7 @@ class ThrottlingTests(TestCase):
|
|||
(0, 'status=SUCCESS; next=60.00 sec'),
|
||||
(0, 'status=FAILURE; next=60.00 sec')
|
||||
))
|
||||
|
||||
|
||||
def test_next_rate_remains_constant_if_followed(self):
|
||||
"""
|
||||
If a client follows the recommended next request rate,
|
||||
|
|
|
@ -22,7 +22,7 @@ class TestDisabledValidations(TestCase):
|
|||
resource = DisabledFormResource
|
||||
|
||||
view = MockView()
|
||||
content = {'qwerty':'uiop'}
|
||||
content = {'qwerty':'uiop'}
|
||||
self.assertEqual(FormResource(view).validate_request(content, None), content)
|
||||
|
||||
def test_disabled_form_validator_get_bound_form_returns_none(self):
|
||||
|
@ -33,12 +33,12 @@ class TestDisabledValidations(TestCase):
|
|||
|
||||
class MockView(View):
|
||||
resource = DisabledFormResource
|
||||
|
||||
|
||||
view = MockView()
|
||||
content = {'qwerty':'uiop'}
|
||||
content = {'qwerty':'uiop'}
|
||||
self.assertEqual(FormResource(view).get_bound_form(content), None)
|
||||
|
||||
|
||||
|
||||
def test_disabled_model_form_validator_returns_content_unchanged(self):
|
||||
"""If the view's form is None and does not have a Resource with a model set then
|
||||
ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
|
||||
|
@ -47,17 +47,17 @@ class TestDisabledValidations(TestCase):
|
|||
resource = ModelResource
|
||||
|
||||
view = DisabledModelFormView()
|
||||
content = {'qwerty':'uiop'}
|
||||
content = {'qwerty':'uiop'}
|
||||
self.assertEqual(ModelResource(view).get_bound_form(content), None)#
|
||||
|
||||
def test_disabled_model_form_validator_get_bound_form_returns_none(self):
|
||||
"""If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
|
||||
class DisabledModelFormView(View):
|
||||
resource = ModelResource
|
||||
|
||||
|
||||
view = DisabledModelFormView()
|
||||
content = {'qwerty':'uiop'}
|
||||
self.assertEqual(ModelResource(view).get_bound_form(content), None)
|
||||
content = {'qwerty':'uiop'}
|
||||
self.assertEqual(ModelResource(view).get_bound_form(content), None)
|
||||
|
||||
class TestNonFieldErrors(TestCase):
|
||||
"""Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
|
||||
|
@ -68,15 +68,15 @@ class TestNonFieldErrors(TestCase):
|
|||
field1 = forms.CharField(required=False)
|
||||
field2 = forms.CharField(required=False)
|
||||
ERROR_TEXT = 'You may not supply both field1 and field2'
|
||||
|
||||
|
||||
def clean(self):
|
||||
if 'field1' in self.cleaned_data and 'field2' in self.cleaned_data:
|
||||
raise forms.ValidationError(self.ERROR_TEXT)
|
||||
return self.cleaned_data #pragma: no cover
|
||||
|
||||
|
||||
class MockResource(FormResource):
|
||||
form = MockForm
|
||||
|
||||
|
||||
class MockView(View):
|
||||
pass
|
||||
|
||||
|
@ -84,7 +84,7 @@ class TestNonFieldErrors(TestCase):
|
|||
content = {'field1': 'example1', 'field2': 'example2'}
|
||||
try:
|
||||
MockResource(view).validate_request(content, None)
|
||||
except ErrorResponse, exc:
|
||||
except ErrorResponse, exc:
|
||||
self.assertEqual(exc.response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
|
||||
else:
|
||||
self.fail('ErrorResponse was not raised') #pragma: no cover
|
||||
|
@ -94,26 +94,26 @@ class TestFormValidation(TestCase):
|
|||
"""Tests which check basic form validation.
|
||||
Also includes the same set of tests with a ModelFormValidator for which the form has been explicitly set.
|
||||
(ModelFormValidator should behave as FormValidator if a form is set rather than relying on the default ModelForm)"""
|
||||
def setUp(self):
|
||||
def setUp(self):
|
||||
class MockForm(forms.Form):
|
||||
qwerty = forms.CharField(required=True)
|
||||
|
||||
class MockFormResource(FormResource):
|
||||
form = MockForm
|
||||
|
||||
|
||||
class MockModelResource(ModelResource):
|
||||
form = MockForm
|
||||
|
||||
|
||||
class MockFormView(View):
|
||||
resource = MockFormResource
|
||||
|
||||
|
||||
class MockModelFormView(View):
|
||||
resource = MockModelResource
|
||||
|
||||
self.MockFormResource = MockFormResource
|
||||
self.MockModelResource = MockModelResource
|
||||
self.MockModelResource = MockModelResource
|
||||
self.MockFormView = MockFormView
|
||||
self.MockModelFormView = MockModelFormView
|
||||
self.MockModelFormView = MockModelFormView
|
||||
|
||||
|
||||
def validation_returns_content_unchanged_if_already_valid_and_clean(self, validator):
|
||||
|
@ -130,22 +130,22 @@ class TestFormValidation(TestCase):
|
|||
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
|
||||
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
|
||||
broken clients more easily (eg submitting content with a misnamed field)"""
|
||||
content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
self.assertRaises(ErrorResponse, validator.validate_request, content, None)
|
||||
|
||||
def validation_allows_extra_fields_if_explicitly_set(self, validator):
|
||||
"""If we include an allowed_extra_fields paramater on _validate, then allow fields with those names."""
|
||||
content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
validator._validate(content, None, allowed_extra_fields=('extra',))
|
||||
|
||||
def validation_does_not_require_extra_fields_if_explicitly_set(self, validator):
|
||||
"""If we include an allowed_extra_fields paramater on _validate, then do not fail if we do not have fields with those names."""
|
||||
content = {'qwerty': 'uiop'}
|
||||
content = {'qwerty': 'uiop'}
|
||||
self.assertEqual(validator._validate(content, None, allowed_extra_fields=('extra',)), content)
|
||||
|
||||
def validation_failed_due_to_no_content_returns_appropriate_message(self, validator):
|
||||
"""If validation fails due to no content, ensure the response contains a single non-field error"""
|
||||
content = {}
|
||||
content = {}
|
||||
try:
|
||||
validator.validate_request(content, None)
|
||||
except ErrorResponse, exc:
|
||||
|
@ -158,7 +158,7 @@ class TestFormValidation(TestCase):
|
|||
content = {'qwerty': ''}
|
||||
try:
|
||||
validator.validate_request(content, None)
|
||||
except ErrorResponse, exc:
|
||||
except ErrorResponse, exc:
|
||||
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.']}})
|
||||
else:
|
||||
self.fail('ResourceException was not raised') #pragma: no cover
|
||||
|
@ -168,17 +168,17 @@ class TestFormValidation(TestCase):
|
|||
content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
try:
|
||||
validator.validate_request(content, None)
|
||||
except ErrorResponse, exc:
|
||||
except ErrorResponse, exc:
|
||||
self.assertEqual(exc.response.raw_content, {'field-errors': {'extra': ['This field does not exist.']}})
|
||||
else:
|
||||
self.fail('ResourceException was not raised') #pragma: no cover
|
||||
|
||||
|
||||
def validation_failed_due_to_multiple_errors_returns_appropriate_message(self, validator):
|
||||
"""If validation for multiple reasons, ensure the response contains each error"""
|
||||
content = {'qwerty': '', 'extra': 'extra'}
|
||||
try:
|
||||
validator.validate_request(content, None)
|
||||
except ErrorResponse, exc:
|
||||
except ErrorResponse, exc:
|
||||
self.assertEqual(exc.response.raw_content, {'field-errors': {'qwerty': ['This field is required.'],
|
||||
'extra': ['This field does not exist.']}})
|
||||
else:
|
||||
|
@ -263,23 +263,23 @@ class TestFormValidation(TestCase):
|
|||
|
||||
class TestModelFormValidator(TestCase):
|
||||
"""Tests specific to ModelFormValidatorMixin"""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Create a validator for a model with two fields and a property."""
|
||||
"""Create a validator for a model with two fields and a property."""
|
||||
class MockModel(models.Model):
|
||||
qwerty = models.CharField(max_length=256)
|
||||
uiop = models.CharField(max_length=256, blank=True)
|
||||
|
||||
|
||||
@property
|
||||
def readonly(self):
|
||||
return 'read only'
|
||||
|
||||
|
||||
class MockResource(ModelResource):
|
||||
model = MockModel
|
||||
|
||||
|
||||
class MockView(View):
|
||||
resource = MockResource
|
||||
|
||||
|
||||
self.validator = MockResource(MockView)
|
||||
|
||||
|
||||
|
@ -299,19 +299,19 @@ class TestModelFormValidator(TestCase):
|
|||
broken clients more easily (eg submitting content with a misnamed field)"""
|
||||
content = {'qwerty': 'example', 'uiop':'example', 'readonly': 'read only', 'extra': 'extra'}
|
||||
self.assertRaises(ErrorResponse, self.validator.validate_request, content, None)
|
||||
|
||||
|
||||
def test_validate_requires_fields_on_model_forms(self):
|
||||
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
|
||||
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
|
||||
broken clients more easily (eg submitting content with a misnamed field)"""
|
||||
content = {'readonly': 'read only'}
|
||||
content = {'readonly': 'read only'}
|
||||
self.assertRaises(ErrorResponse, self.validator.validate_request, content, None)
|
||||
|
||||
|
||||
def test_validate_does_not_require_blankable_fields_on_model_forms(self):
|
||||
"""Test standard ModelForm validation behaviour - fields with blank=True are not required."""
|
||||
content = {'qwerty':'example', 'readonly': 'read only'}
|
||||
self.validator.validate_request(content, None)
|
||||
|
||||
|
||||
def test_model_form_validator_uses_model_forms(self):
|
||||
self.assertTrue(isinstance(self.validator.get_bound_form(), forms.ModelForm))
|
||||
|
||||
|
|
|
@ -23,17 +23,17 @@ class ResourceMockView(View):
|
|||
foo = forms.BooleanField(required=False)
|
||||
bar = forms.IntegerField(help_text='Must be an integer.')
|
||||
baz = forms.CharField(max_length=32)
|
||||
|
||||
|
||||
form = MockForm
|
||||
|
||||
class MockResource(ModelResource):
|
||||
"""This is a mock model-based resource"""
|
||||
|
||||
|
||||
class MockResourceModel(models.Model):
|
||||
foo = models.BooleanField()
|
||||
bar = models.IntegerField(help_text='Must be an integer.')
|
||||
baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
|
||||
|
||||
|
||||
model = MockResourceModel
|
||||
fields = ('foo', 'bar', 'baz')
|
||||
|
||||
|
@ -50,62 +50,62 @@ urlpatterns = patterns('djangorestframework.utils.staticviews',
|
|||
|
||||
class BaseViewTests(TestCase):
|
||||
"""Test the base view class of djangorestframework"""
|
||||
urls = 'djangorestframework.tests.views'
|
||||
|
||||
urls = 'djangorestframework.tests.views'
|
||||
|
||||
def test_options_method_simple_view(self):
|
||||
response = self.client.options('/mock/')
|
||||
self._verify_options_response(response,
|
||||
name='Mock',
|
||||
self._verify_options_response(response,
|
||||
name='Mock',
|
||||
description='This is a basic mock view')
|
||||
|
||||
|
||||
def test_options_method_resource_view(self):
|
||||
response = self.client.options('/resourcemock/')
|
||||
self._verify_options_response(response,
|
||||
name='Resource Mock',
|
||||
description='This is a resource-based mock view',
|
||||
fields={'foo':'BooleanField',
|
||||
'bar':'IntegerField',
|
||||
'baz':'CharField',
|
||||
self._verify_options_response(response,
|
||||
name='Resource Mock',
|
||||
description='This is a resource-based mock view',
|
||||
fields={'foo':'BooleanField',
|
||||
'bar':'IntegerField',
|
||||
'baz':'CharField',
|
||||
})
|
||||
|
||||
|
||||
def test_options_method_model_resource_list_view(self):
|
||||
response = self.client.options('/model/')
|
||||
self._verify_options_response(response,
|
||||
name='Mock List',
|
||||
description='This is a mock model-based resource',
|
||||
fields={'foo':'BooleanField',
|
||||
'bar':'IntegerField',
|
||||
'baz':'CharField',
|
||||
self._verify_options_response(response,
|
||||
name='Mock List',
|
||||
description='This is a mock model-based resource',
|
||||
fields={'foo':'BooleanField',
|
||||
'bar':'IntegerField',
|
||||
'baz':'CharField',
|
||||
})
|
||||
|
||||
def test_options_method_model_resource_detail_view(self):
|
||||
response = self.client.options('/model/0/')
|
||||
self._verify_options_response(response,
|
||||
name='Mock Instance',
|
||||
description='This is a mock model-based resource',
|
||||
fields={'foo':'BooleanField',
|
||||
'bar':'IntegerField',
|
||||
'baz':'CharField',
|
||||
self._verify_options_response(response,
|
||||
name='Mock Instance',
|
||||
description='This is a mock model-based resource',
|
||||
fields={'foo':'BooleanField',
|
||||
'bar':'IntegerField',
|
||||
'baz':'CharField',
|
||||
})
|
||||
|
||||
def _verify_options_response(self, response, name, description, fields=None, status=200,
|
||||
def _verify_options_response(self, response, name, description, fields=None, status=200,
|
||||
mime_type='application/json'):
|
||||
self.assertEqual(response.status_code, status)
|
||||
self.assertEqual(response['Content-Type'].split(';')[0], mime_type)
|
||||
parser = JSONParser(None)
|
||||
(data, files) = parser.parse(StringIO(response.content))
|
||||
self.assertTrue('application/json' in data['renders'])
|
||||
self.assertEqual(name, data['name'])
|
||||
self.assertEqual(description, data['description'])
|
||||
self.assertTrue('application/json' in data['renders'])
|
||||
self.assertEqual(name, data['name'])
|
||||
self.assertEqual(description, data['description'])
|
||||
if fields is None:
|
||||
self.assertFalse(hasattr(data, 'fields'))
|
||||
else:
|
||||
self.assertEqual(data['fields'], fields)
|
||||
self.assertEqual(data['fields'], fields)
|
||||
|
||||
|
||||
class ExtraViewsTests(TestCase):
|
||||
"""Test the extra views djangorestframework provides"""
|
||||
urls = 'djangorestframework.tests.views'
|
||||
urls = 'djangorestframework.tests.views'
|
||||
|
||||
def test_robots_view(self):
|
||||
"""Ensure the robots view exists"""
|
||||
|
|
|
@ -36,7 +36,7 @@ def as_tuple(obj):
|
|||
return obj
|
||||
return (obj,)
|
||||
|
||||
|
||||
|
||||
def url_resolves(url):
|
||||
"""
|
||||
Return True if the given URL is mapped to a view in the urlconf, False otherwise.
|
||||
|
@ -50,7 +50,7 @@ def url_resolves(url):
|
|||
|
||||
# From http://www.koders.com/python/fidB6E125C586A6F49EAC38992CF3AFDAAE35651975.aspx?s=mdef:xml
|
||||
#class object_dict(dict):
|
||||
# """object view of dict, you can
|
||||
# """object view of dict, you can
|
||||
# >>> a = object_dict()
|
||||
# >>> a.fish = 'fish'
|
||||
# >>> a['fish']
|
||||
|
@ -103,8 +103,8 @@ class XML2Dict(object):
|
|||
old = node_tree[tag]
|
||||
if not isinstance(old, list):
|
||||
node_tree.pop(tag)
|
||||
node_tree[tag] = [old] # multi times, so change old dict to a list
|
||||
node_tree[tag].append(tree) # add the new one
|
||||
node_tree[tag] = [old] # multi times, so change old dict to a list
|
||||
node_tree[tag].append(tree) # add the new one
|
||||
|
||||
return node_tree
|
||||
|
||||
|
@ -117,13 +117,13 @@ class XML2Dict(object):
|
|||
"""
|
||||
result = re.compile("\{(.*)\}(.*)").search(tag)
|
||||
if result:
|
||||
value.namespace, tag = result.groups()
|
||||
value.namespace, tag = result.groups()
|
||||
return (tag, value)
|
||||
|
||||
def parse(self, file):
|
||||
"""parse a xml file to a dict"""
|
||||
f = open(file, 'r')
|
||||
return self.fromstring(f.read())
|
||||
return self.fromstring(f.read())
|
||||
|
||||
def fromstring(self, s):
|
||||
"""parse a string"""
|
||||
|
@ -150,16 +150,16 @@ class XMLRenderer():
|
|||
xml.startElement(key, {})
|
||||
self._to_xml(xml, value)
|
||||
xml.endElement(key)
|
||||
|
||||
|
||||
elif data is None:
|
||||
# Don't output any value
|
||||
pass
|
||||
pass
|
||||
|
||||
else:
|
||||
xml.characters(smart_unicode(data))
|
||||
|
||||
def dict2xml(self, data):
|
||||
stream = StringIO.StringIO()
|
||||
stream = StringIO.StringIO()
|
||||
|
||||
xml = SimplerXMLGenerator(stream, "utf-8")
|
||||
xml.startDocument()
|
||||
|
|
|
@ -3,12 +3,12 @@ from djangorestframework.utils.description import get_name
|
|||
|
||||
def get_breadcrumbs(url):
|
||||
"""Given a url returns a list of breadcrumbs, which are each a tuple of (name, url)."""
|
||||
|
||||
|
||||
from djangorestframework.views import View
|
||||
|
||||
|
||||
def breadcrumbs_recursive(url, breadcrumbs_list):
|
||||
"""Add tuples of (name, url) to the breadcrumbs list, progressively chomping off parts of the url."""
|
||||
|
||||
|
||||
try:
|
||||
(view, unused_args, unused_kwargs) = resolve(url)
|
||||
except:
|
||||
|
@ -17,15 +17,15 @@ def get_breadcrumbs(url):
|
|||
# Check if this is a REST framework view, and if so add it to the breadcrumbs
|
||||
if isinstance(getattr(view, 'cls_instance', None), View):
|
||||
breadcrumbs_list.insert(0, (get_name(view), url))
|
||||
|
||||
|
||||
if url == '':
|
||||
# All done
|
||||
return breadcrumbs_list
|
||||
|
||||
|
||||
elif url.endswith('/'):
|
||||
# Drop trailing slash off the end and continue to try to resolve more breadcrumbs
|
||||
return breadcrumbs_recursive(url.rstrip('/'), breadcrumbs_list)
|
||||
|
||||
|
||||
# Drop trailing non-slash off the end and continue to try to resolve more breadcrumbs
|
||||
return breadcrumbs_recursive(url[:url.rfind('/') + 1], breadcrumbs_list)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from djangorestframework.resources import Resource, FormResource, ModelResource
|
|||
def get_name(view):
|
||||
"""
|
||||
Return a name for the view.
|
||||
|
||||
|
||||
If view has a name attribute, use that, otherwise use the view's class name, with 'CamelCaseNames' converted to 'Camel Case Names'.
|
||||
"""
|
||||
|
||||
|
@ -22,7 +22,7 @@ def get_name(view):
|
|||
# If this view has a resource that's been overridden, then use that resource for the name
|
||||
if getattr(view, 'resource', None) not in (None, Resource, FormResource, ModelResource):
|
||||
name = view.resource.__name__
|
||||
|
||||
|
||||
# Chomp of any non-descriptive trailing part of the resource class name
|
||||
if name.endswith('Resource') and name != 'Resource':
|
||||
name = name[:-len('Resource')]
|
||||
|
@ -30,7 +30,7 @@ def get_name(view):
|
|||
# If the view has a descriptive suffix, eg '*** List', '*** Instance'
|
||||
if getattr(view, '_suffix', None):
|
||||
name += view._suffix
|
||||
|
||||
|
||||
# Otherwise if it's a function view use the function's name
|
||||
elif getattr(view, '__name__', None) is not None:
|
||||
name = view.__name__
|
||||
|
@ -62,12 +62,12 @@ def get_description(view):
|
|||
# grok the class instance that we stored when as_view was called.
|
||||
if getattr(view, 'cls_instance', None):
|
||||
view = view.cls_instance
|
||||
|
||||
|
||||
|
||||
# If this view has a resource that's been overridden, then use the resource's doctring
|
||||
if getattr(view, 'resource', None) not in (None, Resource, FormResource, ModelResource):
|
||||
doc = view.resource.__doc__
|
||||
|
||||
|
||||
# Otherwise use the view doctring
|
||||
elif getattr(view, '__doc__', None):
|
||||
doc = view.__doc__
|
||||
|
@ -81,11 +81,11 @@ def get_description(view):
|
|||
|
||||
whitespace_counts = [len(line) - len(line.lstrip(' ')) for line in doc.splitlines()[1:] if line.lstrip()]
|
||||
|
||||
# unindent the docstring if needed
|
||||
# unindent the docstring if needed
|
||||
if whitespace_counts:
|
||||
whitespace_pattern = '^' + (' ' * min(whitespace_counts))
|
||||
return re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', doc)
|
||||
|
||||
# otherwise return it as-is
|
||||
return doc
|
||||
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ class _MediaType(object):
|
|||
# return Decimal(self.params.get('q', '1.0'))
|
||||
# except:
|
||||
# return Decimal(0)
|
||||
|
||||
|
||||
#def score(self):
|
||||
# """
|
||||
# Return an overall score for a given media type given it's quality and precedence.
|
||||
|
@ -119,7 +119,7 @@ class _MediaType(object):
|
|||
# # NB. quality values should only have up to 3 decimal points
|
||||
# # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.9
|
||||
# return self.quality * 10000 + self.precedence
|
||||
|
||||
|
||||
#def as_tuple(self):
|
||||
# return (self.main_type, self.sub_type, self.params)
|
||||
|
||||
|
|
|
@ -12,16 +12,16 @@ class BlogPostResource(ModelResource):
|
|||
ordering = ('-created',)
|
||||
|
||||
def comments(self, instance):
|
||||
return reverse('comments', kwargs={'blogpost': instance.key})
|
||||
return reverse('comments', kwargs={'blogpost': instance.key})
|
||||
|
||||
|
||||
class CommentResource(ModelResource):
|
||||
"""
|
||||
A Comment is associated with a given Blog Post and has a *username* and *comment*, and optionally a *rating*.
|
||||
A Comment is associated with a given Blog Post and has a *username* and *comment*, and optionally a *rating*.
|
||||
"""
|
||||
model = Comment
|
||||
fields = ('username', 'comment', 'created', 'rating', 'url', 'blogpost')
|
||||
ordering = ('-created',)
|
||||
|
||||
|
||||
def blogpost(self, instance):
|
||||
return reverse('blog-post', kwargs={'key': instance.blogpost.key})
|
||||
return reverse('blog-post', kwargs={'key': instance.blogpost.key})
|
|
@ -15,68 +15,68 @@ from blogpost import models, urls
|
|||
|
||||
# class AcceptHeaderTests(TestCase):
|
||||
# """Test correct behaviour of the Accept header as specified by RFC 2616:
|
||||
#
|
||||
#
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"""
|
||||
#
|
||||
#
|
||||
# def assert_accept_mimetype(self, mimetype, expect=None):
|
||||
# """Assert that a request with given mimetype in the accept header,
|
||||
# gives a response with the appropriate content-type."""
|
||||
# if expect is None:
|
||||
# expect = mimetype
|
||||
#
|
||||
#
|
||||
# resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT=mimetype)
|
||||
#
|
||||
#
|
||||
# self.assertEquals(resp['content-type'], expect)
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# def dont_test_accept_json(self):
|
||||
# """Ensure server responds with Content-Type of JSON when requested."""
|
||||
# self.assert_accept_mimetype('application/json')
|
||||
#
|
||||
#
|
||||
# def dont_test_accept_xml(self):
|
||||
# """Ensure server responds with Content-Type of XML when requested."""
|
||||
# self.assert_accept_mimetype('application/xml')
|
||||
#
|
||||
#
|
||||
# def dont_test_accept_json_when_prefered_to_xml(self):
|
||||
# """Ensure server responds with Content-Type of JSON when it is the client's prefered choice."""
|
||||
# self.assert_accept_mimetype('application/json;q=0.9, application/xml;q=0.1', expect='application/json')
|
||||
#
|
||||
#
|
||||
# def dont_test_accept_xml_when_prefered_to_json(self):
|
||||
# """Ensure server responds with Content-Type of XML when it is the client's prefered choice."""
|
||||
# self.assert_accept_mimetype('application/json;q=0.1, application/xml;q=0.9', expect='application/xml')
|
||||
#
|
||||
#
|
||||
# def dont_test_default_json_prefered(self):
|
||||
# """Ensure server responds with JSON in preference to XML."""
|
||||
# self.assert_accept_mimetype('application/json,application/xml', expect='application/json')
|
||||
#
|
||||
#
|
||||
# def dont_test_accept_generic_subtype_format(self):
|
||||
# """Ensure server responds with an appropriate type, when the subtype is left generic."""
|
||||
# self.assert_accept_mimetype('text/*', expect='text/html')
|
||||
#
|
||||
#
|
||||
# def dont_test_accept_generic_type_format(self):
|
||||
# """Ensure server responds with an appropriate type, when the type and subtype are left generic."""
|
||||
# self.assert_accept_mimetype('*/*', expect='application/json')
|
||||
#
|
||||
#
|
||||
# def dont_test_invalid_accept_header_returns_406(self):
|
||||
# """Ensure server returns a 406 (not acceptable) response if we set the Accept header to junk."""
|
||||
# resp = self.client.get(reverse(views.RootResource), HTTP_ACCEPT='invalid/invalid')
|
||||
# self.assertNotEquals(resp['content-type'], 'invalid/invalid')
|
||||
# self.assertEquals(resp.status_code, 406)
|
||||
#
|
||||
#
|
||||
# def dont_test_prefer_specific_over_generic(self): # This test is broken right now
|
||||
# """More specific accept types have precedence over less specific types."""
|
||||
# self.assert_accept_mimetype('application/xml, */*', expect='application/xml')
|
||||
# self.assert_accept_mimetype('*/*, application/xml', expect='application/xml')
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# class AllowedMethodsTests(TestCase):
|
||||
# """Basic tests to check that only allowed operations may be performed on a Resource"""
|
||||
#
|
||||
#
|
||||
# def dont_test_reading_a_read_only_resource_is_allowed(self):
|
||||
# """GET requests on a read only resource should default to a 200 (OK) response"""
|
||||
# resp = self.client.get(reverse(views.RootResource))
|
||||
# self.assertEquals(resp.status_code, 200)
|
||||
#
|
||||
#
|
||||
# def dont_test_writing_to_read_only_resource_is_not_allowed(self):
|
||||
# """PUT requests on a read only resource should default to a 405 (method not allowed) response"""
|
||||
# resp = self.client.put(reverse(views.RootResource), {})
|
||||
|
@ -171,7 +171,7 @@ from blogpost import models, urls
|
|||
|
||||
|
||||
class TestRotation(TestCase):
|
||||
"""For the example the maximum amount of Blogposts is capped off at views.MAX_POSTS.
|
||||
"""For the example the maximum amount of Blogposts is capped off at views.MAX_POSTS.
|
||||
Whenever a new Blogpost is posted the oldest one should be popped."""
|
||||
|
||||
def setUp(self):
|
||||
|
@ -193,7 +193,7 @@ class TestRotation(TestCase):
|
|||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
||||
view(request)
|
||||
self.assertEquals(len(models.BlogPost.objects.all()),models.MAX_POSTS)
|
||||
|
||||
|
||||
def test_fifo_behaviour(self):
|
||||
'''It's fine that the Blogposts are capped off at MAX_POSTS. But we want to make sure we see FIFO behaviour.'''
|
||||
for post in range(15):
|
||||
|
@ -201,11 +201,10 @@ class TestRotation(TestCase):
|
|||
request = self.factory.post('/blog-post', data=form_data)
|
||||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
||||
view(request)
|
||||
request = self.factory.get('/blog-post')
|
||||
request = self.factory.get('/blog-post')
|
||||
view = ListOrCreateModelView.as_view(resource=urls.BlogPostResource)
|
||||
response = view(request)
|
||||
response_posts = json.loads(response.content)
|
||||
response_titles = [d['title'] for d in response_posts]
|
||||
response_titles.reverse()
|
||||
self.assertEquals(response_titles, ['%s' % i for i in range(models.MAX_POSTS - 5, models.MAX_POSTS + 5)])
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from djangorestframework.compat import View # Use Django 1.3's django.views.generic.View, or fall back to a clone of that if Django < 1.3
|
||||
from djangorestframework.compat import View # Use Django 1.3's django.views.generic.View, or fall back to a clone of that if Django < 1.3
|
||||
from djangorestframework.mixins import ResponseMixin
|
||||
from djangorestframework.renderers import DEFAULT_RENDERERS
|
||||
from djangorestframework.response import Response
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.conf.urls.defaults import patterns, url
|
||||
from objectstore.views import ObjectStoreRoot, StoredObject
|
||||
|
||||
|
||||
urlpatterns = patterns('objectstore.views',
|
||||
url(r'^$', ObjectStoreRoot.as_view(), name='object-store-root'),
|
||||
url(r'^$', ObjectStoreRoot.as_view(), name='object-store-root'),
|
||||
url(r'^(?P<key>[A-Za-z0-9_-]{1,64})/$', StoredObject.as_view(), name='stored-object'),
|
||||
)
|
||||
|
|
|
@ -39,7 +39,7 @@ class ObjectStoreRoot(View):
|
|||
ctime_sorted_basenames = [item[0] for item in sorted([(os.path.basename(path), os.path.getctime(path)) for path in filepaths],
|
||||
key=operator.itemgetter(1), reverse=True)]
|
||||
return [reverse('stored-object', kwargs={'key':key}) for key in ctime_sorted_basenames]
|
||||
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
Create a new stored object, with a unique key.
|
||||
|
|
|
@ -6,32 +6,32 @@ class PermissionsExampleView(View):
|
|||
"""
|
||||
A container view for permissions examples.
|
||||
"""
|
||||
|
||||
|
||||
def get(self, request):
|
||||
return [{'name': 'Throttling Example', 'url': reverse('throttled-resource')},
|
||||
{'name': 'Logged in example', 'url': reverse('loggedin-resource')},]
|
||||
|
||||
|
||||
|
||||
class ThrottlingExampleView(View):
|
||||
"""
|
||||
A basic read-only View that has a **per-user throttle** of 10 requests per minute.
|
||||
|
||||
|
||||
If a user exceeds the 10 requests limit within a period of one minute, the
|
||||
throttle will be applied until 60 seconds have passed since the first request.
|
||||
"""
|
||||
|
||||
|
||||
permissions = ( PerUserThrottling, )
|
||||
throttle = '10/min'
|
||||
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
Handle GET requests.
|
||||
"""
|
||||
return "Successful response to GET request because throttle is not yet active."
|
||||
|
||||
|
||||
class LoggedInExampleView(View):
|
||||
"""
|
||||
You can login with **'test', 'test'.**
|
||||
You can login with **'test', 'test'.**
|
||||
"""
|
||||
permissions = (IsAuthenticated, )
|
||||
def get(self, request):
|
||||
|
|
|
@ -13,7 +13,7 @@ class PygmentsForm(forms.Form):
|
|||
|
||||
code = forms.CharField(widget=forms.Textarea,
|
||||
label='Code Text',
|
||||
max_length=1000000,
|
||||
max_length=1000000,
|
||||
help_text='(Copy and paste the code text here.)')
|
||||
title = forms.CharField(required=False,
|
||||
help_text='(Optional)',
|
||||
|
|
|
@ -46,4 +46,3 @@ class TestPygmentsExample(TestCase):
|
|||
self.assertEquals(locations, response_locations)
|
||||
|
||||
|
||||
|
|
@ -2,6 +2,6 @@ from django.conf.urls.defaults import patterns, url
|
|||
from pygments_api.views import PygmentsRoot, PygmentsInstance
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', PygmentsRoot.as_view(), name='pygments-root'),
|
||||
url(r'^$', PygmentsRoot.as_view(), name='pygments-root'),
|
||||
url(r'^([a-zA-Z0-9-]+)/$', PygmentsInstance.as_view(), name='pygments-instance'),
|
||||
)
|
||||
|
|
|
@ -72,10 +72,10 @@ class PygmentsRoot(View):
|
|||
linenos = 'table' if self.CONTENT['linenos'] else False
|
||||
options = {'title': self.CONTENT['title']} if self.CONTENT['title'] else {}
|
||||
formatter = HtmlFormatter(style=self.CONTENT['style'], linenos=linenos, full=True, **options)
|
||||
|
||||
|
||||
with open(pathname, 'w') as outfile:
|
||||
highlight(self.CONTENT['code'], lexer, formatter, outfile)
|
||||
|
||||
|
||||
remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
|
||||
|
||||
return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', args=[unique_id])})
|
||||
|
|
|
@ -34,7 +34,7 @@ class AnotherExampleView(View):
|
|||
if int(num) > 2:
|
||||
return Response(status.HTTP_404_NOT_FOUND)
|
||||
return "GET request to AnotherExampleResource %s" % num
|
||||
|
||||
|
||||
def post(self, request, num):
|
||||
"""
|
||||
Handle POST requests, with form validation.
|
||||
|
|
|
@ -8,7 +8,7 @@ from coverage import coverage
|
|||
|
||||
def main():
|
||||
"""Run the tests for the examples and generate a coverage report."""
|
||||
|
||||
|
||||
# Discover the list of all modules that we should test coverage for
|
||||
project_dir = os.path.dirname(__file__)
|
||||
cov_files = []
|
||||
|
@ -18,7 +18,7 @@ def main():
|
|||
continue
|
||||
cov_files.extend([os.path.join(path, file) for file in files if file.endswith('.py')])
|
||||
TestRunner = get_runner(settings)
|
||||
|
||||
|
||||
cov = coverage()
|
||||
cov.erase()
|
||||
cov.start()
|
||||
|
|
|
@ -14,8 +14,8 @@ class Sandbox(View):
|
|||
bash: curl -X GET http://api.django-rest-framework.org/ # (Use default renderer)
|
||||
bash: curl -X GET http://api.django-rest-framework.org/ -H 'Accept: text/plain' # (Use plaintext documentation renderer)
|
||||
|
||||
The examples provided:
|
||||
|
||||
The examples provided:
|
||||
|
||||
1. A basic example using the [Resource](http://django-rest-framework.org/library/resource.html) class.
|
||||
2. A basic example using the [ModelResource](http://django-rest-framework.org/library/modelresource.html) class.
|
||||
3. An basic example using Django 1.3's [class based views](http://docs.djangoproject.com/en/dev/topics/class-based-views/) and djangorestframework's [RendererMixin](http://django-rest-framework.org/library/renderers.html).
|
||||
|
|
|
@ -51,7 +51,7 @@ MEDIA_ROOT = 'media/'
|
|||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash if there is a path component (optional in other cases).
|
||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
||||
# NOTE: None of the djangorestframework examples serve media content via MEDIA_URL.
|
||||
# NOTE: None of the djangorestframework examples serve media content via MEDIA_URL.
|
||||
MEDIA_URL = ''
|
||||
|
||||
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
||||
|
@ -90,10 +90,10 @@ TEMPLATE_DIRS = (
|
|||
)
|
||||
|
||||
# for loading initial data
|
||||
##SERIALIZATION_MODULES = {
|
||||
##SERIALIZATION_MODULES = {
|
||||
# 'yml': "django.core.serializers.pyyaml"
|
||||
|
||||
#}
|
||||
#}
|
||||
|
||||
|
||||
INSTALLED_APPS = (
|
||||
|
|
Loading…
Reference in New Issue
Block a user