aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework
diff options
context:
space:
mode:
Diffstat (limited to 'djangorestframework')
-rw-r--r--djangorestframework/__init__.py3
-rw-r--r--djangorestframework/authentication.py132
-rw-r--r--djangorestframework/authtoken/__init__.py0
-rw-r--r--djangorestframework/authtoken/migrations/0001_initial.py72
-rw-r--r--djangorestframework/authtoken/migrations/__init__.py0
-rw-r--r--djangorestframework/authtoken/models.py23
-rw-r--r--djangorestframework/authtoken/views.py0
-rw-r--r--djangorestframework/compat.py480
-rw-r--r--djangorestframework/decorators.py94
-rw-r--r--djangorestframework/exceptions.py62
-rw-r--r--djangorestframework/fields.py446
-rw-r--r--djangorestframework/generics.py113
-rw-r--r--djangorestframework/mixins.py97
-rw-r--r--djangorestframework/models.py1
-rw-r--r--djangorestframework/parsers.py260
-rw-r--r--djangorestframework/permissions.py116
-rw-r--r--djangorestframework/renderers.py408
-rw-r--r--djangorestframework/request.py284
-rw-r--r--djangorestframework/response.py173
-rw-r--r--djangorestframework/reverse.py20
-rw-r--r--djangorestframework/runtests/__init__.py0
-rwxr-xr-xdjangorestframework/runtests/runcoverage.py66
-rwxr-xr-xdjangorestframework/runtests/runtests.py40
-rw-r--r--djangorestframework/runtests/settings.py118
-rw-r--r--djangorestframework/runtests/urls.py7
-rw-r--r--djangorestframework/serializers.py348
-rw-r--r--djangorestframework/settings.py125
-rw-r--r--djangorestframework/static/djangorestframework/css/style.css1209
-rw-r--r--djangorestframework/status.py52
-rw-r--r--djangorestframework/templates/djangorestframework/api.html3
-rw-r--r--djangorestframework/templates/djangorestframework/api.txt8
-rw-r--r--djangorestframework/templates/djangorestframework/base.html151
-rw-r--r--djangorestframework/templates/djangorestframework/login.html45
-rw-r--r--djangorestframework/templatetags/__init__.py0
-rw-r--r--djangorestframework/templatetags/add_query_param.py10
-rw-r--r--djangorestframework/templatetags/optional_login.py32
-rw-r--r--djangorestframework/templatetags/urlize_quoted_links.py102
-rw-r--r--djangorestframework/tests/__init__.py12
-rw-r--r--djangorestframework/tests/accept.py83
-rw-r--r--djangorestframework/tests/authentication.py153
-rw-r--r--djangorestframework/tests/breadcrumbs.py72
-rw-r--r--djangorestframework/tests/description.py113
-rw-r--r--djangorestframework/tests/files.py34
-rw-r--r--djangorestframework/tests/methods.py0
-rw-r--r--djangorestframework/tests/mixins.py285
-rw-r--r--djangorestframework/tests/models.py28
-rw-r--r--djangorestframework/tests/modelviews.py90
-rw-r--r--djangorestframework/tests/oauthentication.py211
-rw-r--r--djangorestframework/tests/package.py11
-rw-r--r--djangorestframework/tests/parsers.py212
-rw-r--r--djangorestframework/tests/renderers.py384
-rw-r--r--djangorestframework/tests/request.py266
-rw-r--r--djangorestframework/tests/response.py309
-rw-r--r--djangorestframework/tests/reverse.py35
-rw-r--r--djangorestframework/tests/serializer.py117
-rw-r--r--djangorestframework/tests/status.py12
-rw-r--r--djangorestframework/tests/testcases.py63
-rw-r--r--djangorestframework/tests/throttling.py144
-rw-r--r--djangorestframework/tests/validators.py329
-rw-r--r--djangorestframework/tests/views.py128
-rw-r--r--djangorestframework/throttling.py217
-rw-r--r--djangorestframework/urlpatterns.py24
-rw-r--r--djangorestframework/urls.py23
-rw-r--r--djangorestframework/utils/__init__.py139
-rw-r--r--djangorestframework/utils/breadcrumbs.py32
-rw-r--r--djangorestframework/utils/encoders.py38
-rw-r--r--djangorestframework/utils/mediatypes.py113
-rw-r--r--djangorestframework/views.py282
68 files changed, 0 insertions, 9059 deletions
diff --git a/djangorestframework/__init__.py b/djangorestframework/__init__.py
deleted file mode 100644
index 557f5943..00000000
--- a/djangorestframework/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-__version__ = '2.0.0'
-
-VERSION = __version__ # synonym
diff --git a/djangorestframework/authentication.py b/djangorestframework/authentication.py
deleted file mode 100644
index a6634392..00000000
--- a/djangorestframework/authentication.py
+++ /dev/null
@@ -1,132 +0,0 @@
-"""
-The :mod:`authentication` module provides a set of pluggable authentication classes.
-
-Authentication behavior is provided by mixing the :class:`mixins.RequestMixin` class into a :class:`View` class.
-"""
-
-from django.contrib.auth import authenticate
-from djangorestframework.compat import CsrfViewMiddleware
-from djangorestframework.authtoken.models import Token
-import base64
-
-
-class BaseAuthentication(object):
- """
- All authentication classes should extend BaseAuthentication.
- """
-
- 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.
-
- 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`.
- """
- return None
-
-
-class BasicAuthentication(BaseAuthentication):
- """
- Base class for HTTP Basic authentication.
- Subclasses should implement `.authenticate_credentials()`.
- """
-
- def authenticate(self, request):
- """
- Returns a `User` if a correct username and password have been supplied
- using HTTP Basic authentication. Otherwise returns `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":
- try:
- auth_parts = base64.b64decode(auth[1]).partition(':')
- except TypeError:
- return None
-
- try:
- userid, password = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
- except DjangoUnicodeDecodeError:
- return None
-
- return self.authenticate_credentials(userid, password)
-
- def authenticate_credentials(self, userid, password):
- """
- Given the Basic authentication userid and password, authenticate
- and return a user instance.
- """
- raise NotImplementedError('.authenticate_credentials() must be overridden')
-
-
-class UserBasicAuthentication(BasicAuthentication):
- def authenticate_credentials(self, userid, password):
- """
- Authenticate the userid and password against username and password.
- """
- user = authenticate(username=userid, password=password)
- if user is not None and user.is_active:
- return (user, None)
-
-
-class SessionAuthentication(BaseAuthentication):
- """
- Use Django's session framework for authentication.
- """
-
- def authenticate(self, request):
- """
- Returns a :obj:`User` if the request session currently has a logged in user.
- Otherwise returns :const:`None`.
- """
- user = getattr(request._request, 'user', None)
-
- if user and user.is_active:
- # Enforce CSRF validation for session based authentication.
- resp = CsrfViewMiddleware().process_view(request, None, (), {})
-
- if resp is None: # csrf passed
- return (user, None)
-
-
-class TokenAuthentication(BaseAuthentication):
- """
- Simple token based authentication.
-
- Clients should authenticate by passing the token key in the "Authorization"
- HTTP header, prepended with the string "Token ". For example:
-
- Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
- """
-
- model = Token
- """
- A custom token model may be used, but must have the following properties.
-
- * key -- The string identifying the token
- * user -- The user to which the token belongs
- """
-
- def authenticate(self, request):
- auth = request.META.get('HTTP_AUTHORIZATION', '').split()
-
- if len(auth) == 2 and auth[0].lower() == "token":
- key = auth[1]
- try:
- token = self.model.objects.get(key=key)
- except self.model.DoesNotExist:
- return None
-
- if token.user.is_active and not getattr(token, 'revoked', False):
- return (token.user, token)
-
-# TODO: OAuthAuthentication
diff --git a/djangorestframework/authtoken/__init__.py b/djangorestframework/authtoken/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/djangorestframework/authtoken/__init__.py
+++ /dev/null
diff --git a/djangorestframework/authtoken/migrations/0001_initial.py b/djangorestframework/authtoken/migrations/0001_initial.py
deleted file mode 100644
index a91006b0..00000000
--- a/djangorestframework/authtoken/migrations/0001_initial.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding model 'Token'
- db.create_table('authtoken_token', (
- ('key', self.gf('django.db.models.fields.CharField')(max_length=40, primary_key=True)),
- ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
- ('revoked', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
- ))
- db.send_create_signal('authtoken', ['Token'])
-
-
- def backwards(self, orm):
- # Deleting model 'Token'
- db.delete_table('authtoken_token')
-
-
- models = {
- 'auth.group': {
- 'Meta': {'object_name': 'Group'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
- 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
- },
- 'auth.permission': {
- 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
- 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- 'auth.user': {
- 'Meta': {'object_name': 'User'},
- 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
- 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
- },
- 'authtoken.token': {
- 'Meta': {'object_name': 'Token'},
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
- 'revoked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
- },
- 'contenttypes.contenttype': {
- 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
- }
- }
-
- complete_apps = ['authtoken'] \ No newline at end of file
diff --git a/djangorestframework/authtoken/migrations/__init__.py b/djangorestframework/authtoken/migrations/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/djangorestframework/authtoken/migrations/__init__.py
+++ /dev/null
diff --git a/djangorestframework/authtoken/models.py b/djangorestframework/authtoken/models.py
deleted file mode 100644
index fd47e6c7..00000000
--- a/djangorestframework/authtoken/models.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import uuid
-import hmac
-from hashlib import sha1
-from django.db import models
-
-
-class Token(models.Model):
- """
- The default authorization token model.
- """
- key = models.CharField(max_length=40, primary_key=True)
- user = models.ForeignKey('auth.User')
- revoked = models.BooleanField(default=False)
- created = models.DateTimeField(auto_now_add=True)
-
- def save(self, *args, **kwargs):
- if not self.key:
- self.key = self.generate_key()
- return super(Token, self).save(*args, **kwargs)
-
- def generate_key(self):
- unique = str(uuid.uuid4())
- return hmac.new(unique, digestmod=sha1).hexdigest()
diff --git a/djangorestframework/authtoken/views.py b/djangorestframework/authtoken/views.py
deleted file mode 100644
index e69de29b..00000000
--- a/djangorestframework/authtoken/views.py
+++ /dev/null
diff --git a/djangorestframework/compat.py b/djangorestframework/compat.py
deleted file mode 100644
index f21dc4ef..00000000
--- a/djangorestframework/compat.py
+++ /dev/null
@@ -1,480 +0,0 @@
-"""
-The :mod:`compat` module provides support for backwards compatibility with older versions of django/python.
-"""
-import django
-
-# cStringIO only if it's available, otherwise StringIO
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
-
-
-# parse_qs from 'urlparse' module unless python 2.5, in which case from 'cgi'
-try:
- # python >= 2.6
- from urlparse import parse_qs
-except ImportError:
- # python < 2.6
- from cgi import parse_qs
-
-
-# django.test.client.RequestFactory (Required for 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,
- just as if that :func:`view` had been hooked up using a URLconf.
- """
- def request(self, **request):
- """
- Similar to parent class, but returns the :obj:`request` object as soon as it
- has created it.
- """
- environ = {
- 'HTTP_COOKIE': self.cookies,
- 'PATH_INFO': '/',
- 'QUERY_STRING': '',
- 'REQUEST_METHOD': 'GET',
- 'SCRIPT_NAME': '',
- 'SERVER_NAME': 'testserver',
- 'SERVER_PORT': 80,
- 'SERVER_PROTOCOL': 'HTTP/1.1',
- }
- environ.update(self.defaults)
- environ.update(request)
- return WSGIRequest(environ)
-
-# django.views.generic.View (Django >= 1.3)
-try:
- from django.views.generic import View
- if not hasattr(View, 'head'):
- # 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
- keyword arguments, and other things.
- """
- # Go through keyword arguments, and either save their values to our
- # 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):
- """
- Main entry point for a request-response process.
- """
- # sanitize keyword arguments
- for key in initkwargs:
- if key in cls.http_method_names:
- raise TypeError(u"You tried to pass in the %s method name as a "
- u"keyword argument to %s(). Don't do that."
- % (key, cls.__name__))
- 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
- # request method isn't on the approved list.
- if request.method.lower() in self.http_method_names:
- handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
- else:
- handler = self.http_method_not_allowed
- self.request = request
- 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),
- # extra={
- # 'status_code': 405,
- # 'request': self.request
- # }
- #)
- return http.HttpResponseNotAllowed(allowed_methods)
-
- def head(self, request, *args, **kwargs):
- return self.get(request, *args, **kwargs)
-
-# PUT, DELETE do not require CSRF until 1.4. They should. Make it better.
-if django.VERSION >= (1, 4):
- from django.middleware.csrf import CsrfViewMiddleware
-else:
- import hashlib
- import re
- import random
- import logging
- import urlparse
-
- from django.conf import settings
- from django.core.urlresolvers import get_callable
-
- try:
- from logging import NullHandler
- except ImportError:
- class NullHandler(logging.Handler):
- def emit(self, record):
- pass
-
- logger = logging.getLogger('django.request')
-
- if not logger.handlers:
- logger.addHandler(NullHandler())
-
- def same_origin(url1, url2):
- """
- Checks if two URLs are 'same-origin'
- """
- p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)
- return p1[0:2] == p2[0:2]
-
- def constant_time_compare(val1, val2):
- """
- Returns True if the two strings are equal, False otherwise.
-
- The time taken is independent of the number of characters that match.
- """
- if len(val1) != len(val2):
- return False
- result = 0
- for x, y in zip(val1, val2):
- result |= ord(x) ^ ord(y)
- return result == 0
-
- # Use the system (hardware-based) random number generator if it exists.
- if hasattr(random, 'SystemRandom'):
- randrange = random.SystemRandom().randrange
- else:
- randrange = random.randrange
- _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63
-
- REASON_NO_REFERER = "Referer checking failed - no Referer."
- REASON_BAD_REFERER = "Referer checking failed - %s does not match %s."
- REASON_NO_CSRF_COOKIE = "CSRF cookie not set."
- REASON_BAD_TOKEN = "CSRF token missing or incorrect."
-
- def _get_failure_view():
- """
- Returns the view to be used for CSRF rejections
- """
- return get_callable(settings.CSRF_FAILURE_VIEW)
-
- def _get_new_csrf_key():
- return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
-
- def get_token(request):
- """
- Returns the the CSRF token required for a POST form. The token is an
- alphanumeric value.
-
- A side effect of calling this function is to make the the csrf_protect
- decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
- header to the outgoing response. For this reason, you may need to use this
- function lazily, as is done by the csrf context processor.
- """
- request.META["CSRF_COOKIE_USED"] = True
- return request.META.get("CSRF_COOKIE", None)
-
- def _sanitize_token(token):
- # Allow only alphanum, and ensure we return a 'str' for the sake of the post
- # processing middleware.
- token = re.sub('[^a-zA-Z0-9]', '', str(token.decode('ascii', 'ignore')))
- if token == "":
- # In case the cookie has been truncated to nothing at some point.
- return _get_new_csrf_key()
- else:
- return token
-
- class CsrfViewMiddleware(object):
- """
- Middleware that requires a present and correct csrfmiddlewaretoken
- for POST requests that have a CSRF cookie, and sets an outgoing
- CSRF cookie.
-
- This middleware should be used in conjunction with the csrf_token template
- tag.
- """
- # The _accept and _reject methods currently only exist for the sake of the
- # requires_csrf_token decorator.
- def _accept(self, request):
- # Avoid checking the request twice by adding a custom attribute to
- # request. This will be relevant when both decorator and middleware
- # are used.
- request.csrf_processing_done = True
- return None
-
- def _reject(self, request, reason):
- return _get_failure_view()(request, reason=reason)
-
- def process_view(self, request, callback, callback_args, callback_kwargs):
-
- if getattr(request, 'csrf_processing_done', False):
- return None
-
- try:
- csrf_token = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME])
- # Use same token next time
- request.META['CSRF_COOKIE'] = csrf_token
- except KeyError:
- csrf_token = None
- # Generate token and store it in the request, so it's available to the view.
- request.META["CSRF_COOKIE"] = _get_new_csrf_key()
-
- # Wait until request.META["CSRF_COOKIE"] has been manipulated before
- # bailing out, so that get_token still works
- if getattr(callback, 'csrf_exempt', False):
- return None
-
- # Assume that anything not defined as 'safe' by RC2616 needs protection.
- if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
- if getattr(request, '_dont_enforce_csrf_checks', False):
- # Mechanism to turn off CSRF checks for test suite. It comes after
- # the creation of CSRF cookies, so that everything else continues to
- # work exactly the same (e.g. cookies are sent etc), but before the
- # any branches that call reject()
- return self._accept(request)
-
- if request.is_secure():
- # Suppose user visits http://example.com/
- # An active network attacker,(man-in-the-middle, MITM) sends a
- # POST form which targets https://example.com/detonate-bomb/ and
- # submits it via javascript.
- #
- # The attacker will need to provide a CSRF cookie and token, but
- # that is no problem for a MITM and the session independent
- # nonce we are using. So the MITM can circumvent the CSRF
- # protection. This is true for any HTTP connection, but anyone
- # using HTTPS expects better! For this reason, for
- # https://example.com/ we need additional protection that treats
- # http://example.com/ as completely untrusted. Under HTTPS,
- # Barth et al. found that the Referer header is missing for
- # same-domain requests in only about 0.2% of cases or less, so
- # we can use strict Referer checking.
- referer = request.META.get('HTTP_REFERER')
- if referer is None:
- logger.warning('Forbidden (%s): %s' % (REASON_NO_REFERER, request.path),
- extra={
- 'status_code': 403,
- 'request': request,
- }
- )
- return self._reject(request, REASON_NO_REFERER)
-
- # Note that request.get_host() includes the port
- good_referer = 'https://%s/' % request.get_host()
- if not same_origin(referer, good_referer):
- reason = REASON_BAD_REFERER % (referer, good_referer)
- logger.warning('Forbidden (%s): %s' % (reason, request.path),
- extra={
- 'status_code': 403,
- 'request': request,
- }
- )
- return self._reject(request, reason)
-
- if csrf_token is None:
- # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
- # and in this way we can avoid all CSRF attacks, including login
- # CSRF.
- logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path),
- extra={
- 'status_code': 403,
- 'request': request,
- }
- )
- return self._reject(request, REASON_NO_CSRF_COOKIE)
-
- # check non-cookie token for match
- request_csrf_token = ""
- if request.method == "POST":
- request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
-
- if request_csrf_token == "":
- # Fall back to X-CSRFToken, to make things easier for AJAX,
- # and possible for PUT/DELETE
- request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
-
- if not constant_time_compare(request_csrf_token, csrf_token):
- logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path),
- extra={
- 'status_code': 403,
- 'request': request,
- }
- )
- return self._reject(request, REASON_BAD_TOKEN)
-
- return self._accept(request)
-
-# timezone support is new in Django 1.4
-try:
- from django.utils import timezone
-except ImportError:
- timezone = None
-
-# dateparse is ALSO new in Django 1.4
-try:
- from django.utils.dateparse import parse_date, parse_datetime
-except ImportError:
- import datetime
- import re
-
- date_re = re.compile(
- r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$'
- )
-
- datetime_re = re.compile(
- r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})'
- r'[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})'
- r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?'
- r'(?P<tzinfo>Z|[+-]\d{1,2}:\d{1,2})?$'
- )
-
- time_re = re.compile(
- r'(?P<hour>\d{1,2}):(?P<minute>\d{1,2})'
- r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?'
- )
-
- def parse_date(value):
- match = date_re.match(value)
- if match:
- kw = dict((k, int(v)) for k, v in match.groupdict().iteritems())
- return datetime.date(**kw)
-
- def parse_time(value):
- match = time_re.match(value)
- if match:
- kw = match.groupdict()
- if kw['microsecond']:
- kw['microsecond'] = kw['microsecond'].ljust(6, '0')
- kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None)
- return datetime.time(**kw)
-
- def parse_datetime(value):
- """Parse datetime, but w/o the timezone awareness in 1.4"""
- match = datetime_re.match(value)
- if match:
- kw = match.groupdict()
- if kw['microsecond']:
- kw['microsecond'] = kw['microsecond'].ljust(6, '0')
- kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None)
- return datetime.datetime(**kw)
-
-# Markdown is optional
-try:
- import markdown
-
- def apply_markdown(text):
- """
- Simple wrapper around :func:`markdown.markdown` to set the base level
- of '#' style headers to <h2>.
- """
-
- extensions = ['headerid(level=2)']
- safe_mode = False,
- md = markdown.Markdown(extensions=extensions, safe_mode=safe_mode)
- return md.convert(text)
-
-except ImportError:
- apply_markdown = None
-
-
-# Yaml is optional
-try:
- import yaml
-except ImportError:
- yaml = None
-
-
-import unittest
-try:
- import unittest.skip
-except ImportError: # python < 2.7
- from unittest import TestCase
- import functools
-
- def skip(reason):
- # Pasted from py27/lib/unittest/case.py
- """
- Unconditionally skip a test.
- """
- def decorator(test_item):
- if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
- @functools.wraps(test_item)
- def skip_wrapper(*args, **kwargs):
- pass
- test_item = skip_wrapper
-
- test_item.__unittest_skip__ = True
- test_item.__unittest_skip_why__ = reason
- return test_item
- return decorator
-
- unittest.skip = skip
-
-
-# xml.etree.parse only throws ParseError for python >= 2.7
-try:
- from xml.etree import ParseError as ETParseError
-except ImportError: # python < 2.7
- ETParseError = None
diff --git a/djangorestframework/decorators.py b/djangorestframework/decorators.py
deleted file mode 100644
index ac976bec..00000000
--- a/djangorestframework/decorators.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from functools import wraps
-from django.utils.decorators import available_attrs
-from djangorestframework.views import APIView
-
-
-class LazyViewCreator(object):
-
- """
- This class is responsible for dynamically creating an APIView subclass that
- will wrap a function-based view. Instances of this class are created
- by the function-based view decorators (below), and each decorator is
- responsible for setting attributes on the instance that will eventually be
- copied onto the final class-based view. The CBV gets created lazily the first
- time it's needed, and then cached for future use.
-
- This is done so that the ordering of stacked decorators is irrelevant.
- """
-
- def __init__(self, wrapped_view):
-
- self.wrapped_view = wrapped_view
-
- # Each item in this dictionary will be copied onto the final
- # class-based view that gets created when this object is called
- self.final_view_attrs = {
- 'http_method_names': APIView.http_method_names,
- 'renderer_classes': APIView.renderer_classes,
- 'parser_classes': APIView.parser_classes,
- 'authentication_classes': APIView.authentication_classes,
- 'throttle_classes': APIView.throttle_classes,
- 'permission_classes': APIView.permission_classes,
- }
- self._cached_view = None
-
- def handler(self, *args, **kwargs):
- return self.wrapped_view(*args, **kwargs)
-
- @property
- def view(self):
- """
- Accessor for the dynamically created class-based view. This will
- be created if necessary and cached for next time.
- """
-
- if self._cached_view is None:
-
- class WrappedAPIView(APIView):
- pass
-
- for attr, value in self.final_view_attrs.items():
- setattr(WrappedAPIView, attr, value)
-
- # Attach the wrapped view function for each of the
- # allowed HTTP methods
- for method in WrappedAPIView.http_method_names:
- setattr(WrappedAPIView, method.lower(), self.handler)
-
- self._cached_view = WrappedAPIView.as_view()
-
- return self._cached_view
-
- def __call__(self, *args, **kwargs):
- """
- This is the actual code that gets run per-request
- """
- return self.view(*args, **kwargs)
-
- @staticmethod
- def maybe_create(func_or_instance):
- """
- If the argument is already an instance of LazyViewCreator,
- just return it. Otherwise, create a new one.
- """
- if isinstance(func_or_instance, LazyViewCreator):
- return func_or_instance
- return LazyViewCreator(func_or_instance)
-
-
-def _create_attribute_setting_decorator(attribute, filter=lambda item: item):
- def decorator(value):
- def inner(func):
- wrapper = LazyViewCreator.maybe_create(func)
- wrapper.final_view_attrs[attribute] = filter(value)
- return wrapper
- return inner
- return decorator
-
-
-api_view = _create_attribute_setting_decorator('http_method_names', filter=lambda methods: [method.lower() for method in methods])
-renderer_classes = _create_attribute_setting_decorator('renderer_classes')
-parser_classes = _create_attribute_setting_decorator('parser_classes')
-authentication_classes = _create_attribute_setting_decorator('authentication_classes')
-throttle_classes = _create_attribute_setting_decorator('throttle_classes')
-permission_classes = _create_attribute_setting_decorator('permission_classes')
diff --git a/djangorestframework/exceptions.py b/djangorestframework/exceptions.py
deleted file mode 100644
index 3f5b23f6..00000000
--- a/djangorestframework/exceptions.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-Handled exceptions raised by REST framework.
-
-In addition Django's built in 403 and 404 exceptions are handled.
-(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
-"""
-from djangorestframework import status
-
-
-class APIException(Exception):
- """
- Base class for REST framework exceptions.
- Subclasses should provide `.status_code` and `.detail` properties.
- """
- pass
-
-
-class ParseError(APIException):
- status_code = status.HTTP_400_BAD_REQUEST
- default_detail = 'Malformed request.'
-
- def __init__(self, detail=None):
- self.detail = detail or self.default_detail
-
-
-class PermissionDenied(APIException):
- status_code = status.HTTP_403_FORBIDDEN
- default_detail = 'You do not have permission to perform this action.'
-
- def __init__(self, detail=None):
- self.detail = detail or self.default_detail
-
-
-class MethodNotAllowed(APIException):
- status_code = status.HTTP_405_METHOD_NOT_ALLOWED
- default_detail = "Method '%s' not allowed."
-
- def __init__(self, method, detail=None):
- self.detail = (detail or self.default_detail) % method
-
-
-class UnsupportedMediaType(APIException):
- status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
- default_detail = "Unsupported media type '%s' in request."
-
- def __init__(self, media_type, detail=None):
- self.detail = (detail or self.default_detail) % media_type
-
-
-class Throttled(APIException):
- status_code = status.HTTP_429_TOO_MANY_REQUESTS
- default_detail = "Request was throttled."
- extra_detail = "Expected available in %d second%s."
-
- def __init__(self, wait=None, detail=None):
- import math
- self.wait = wait and math.ceil(wait) or None
- if wait is not None:
- format = detail or self.default_detail + self.extra_detail
- self.detail = format % (self.wait, self.wait != 1 and 's' or '')
- else:
- self.detail = detail or self.default_detail
diff --git a/djangorestframework/fields.py b/djangorestframework/fields.py
deleted file mode 100644
index 13b0e37d..00000000
--- a/djangorestframework/fields.py
+++ /dev/null
@@ -1,446 +0,0 @@
-import copy
-import datetime
-import inspect
-import warnings
-
-from django.core import validators
-from django.core.exceptions import ValidationError
-from django.conf import settings
-from django.db import DEFAULT_DB_ALIAS
-from django.db.models.related import RelatedObject
-from django.utils.encoding import is_protected_type, smart_unicode
-from django.utils.translation import ugettext_lazy as _
-from djangorestframework.compat import parse_date, parse_datetime
-from djangorestframework.compat import timezone
-
-
-def is_simple_callable(obj):
- """
- True if the object is a callable that takes no arguments.
- """
- return (
- (inspect.isfunction(obj) and not inspect.getargspec(obj)[0]) or
- (inspect.ismethod(obj) and len(inspect.getargspec(obj)[0]) <= 1)
- )
-
-
-class Field(object):
- creation_counter = 0
- default_validators = []
- default_error_messages = {
- 'required': _('This field is required.'),
- 'invalid': _('Invalid value.'),
- }
- empty = ''
-
- def __init__(self, source=None, readonly=False, required=None,
- validators=[], error_messages=None):
- self.parent = None
-
- self.creation_counter = Field.creation_counter
- Field.creation_counter += 1
-
- self.source = source
- self.readonly = readonly
- self.required = not(readonly)
-
- messages = {}
- for c in reversed(self.__class__.__mro__):
- messages.update(getattr(c, 'default_error_messages', {}))
- messages.update(error_messages or {})
- self.error_messages = messages
-
- self.validators = self.default_validators + validators
-
- def initialize(self, parent, model_field=None):
- """
- Called to set up a field prior to field_to_native or field_from_native.
-
- parent - The parent serializer.
- model_field - The model field this field corrosponds to, if one exists.
- """
- self.parent = parent
- self.root = parent.root or parent
- self.context = self.root.context
- if model_field:
- self.model_field = model_field
-
- def validate(self, value):
- pass
- # if value in validators.EMPTY_VALUES and self.required:
- # raise ValidationError(self.error_messages['required'])
-
- def run_validators(self, value):
- if value in validators.EMPTY_VALUES:
- return
- errors = []
- for v in self.validators:
- try:
- v(value)
- except ValidationError as e:
- if hasattr(e, 'code') and e.code in self.error_messages:
- message = self.error_messages[e.code]
- if e.params:
- message = message % e.params
- errors.append(message)
- else:
- errors.extend(e.messages)
- if errors:
- raise ValidationError(errors)
-
- def field_from_native(self, data, field_name, into):
- """
- Given a dictionary and a field name, updates the dictionary `into`,
- with the field and it's deserialized value.
- """
- if self.readonly:
- return
-
- try:
- native = data[field_name]
- except KeyError:
- return # TODO Consider validation behaviour, 'required' opt etc...
-
- value = self.from_native(native)
- if self.source == '*':
- if value:
- into.update(value)
- else:
- self.validate(value)
- self.run_validators(value)
- into[self.source or field_name] = value
-
- def from_native(self, value):
- """
- Reverts a simple representation back to the field's value.
- """
- if hasattr(self, 'model_field'):
- try:
- return self.model_field.rel.to._meta.get_field(self.model_field.rel.field_name).to_python(value)
- except:
- return self.model_field.to_python(value)
- return value
-
- def field_to_native(self, obj, field_name):
- """
- Given and object and a field name, returns the value that should be
- serialized for that field.
- """
- if obj is None:
- return self.empty
-
- if self.source == '*':
- return self.to_native(obj)
-
- self.obj = obj # Need to hang onto this in the case of model fields
- if hasattr(self, 'model_field'):
- return self.to_native(self.model_field._get_val_from_obj(obj))
-
- return self.to_native(getattr(obj, self.source or field_name))
-
- def to_native(self, value):
- """
- Converts the field's value into it's simple representation.
- """
- if is_simple_callable(value):
- value = value()
-
- if is_protected_type(value):
- return value
- elif hasattr(self, 'model_field'):
- return self.model_field.value_to_string(self.obj)
- return smart_unicode(value)
-
- def attributes(self):
- """
- Returns a dictionary of attributes to be used when serializing to xml.
- """
- try:
- return {
- "type": self.model_field.get_internal_type()
- }
- except AttributeError:
- return {}
-
-
-class RelatedField(Field):
- """
- A base class for model related fields or related managers.
-
- Subclass this and override `convert` to define custom behaviour when
- serializing related objects.
- """
-
- def field_to_native(self, obj, field_name):
- obj = getattr(obj, field_name)
- if obj.__class__.__name__ in ('RelatedManager', 'ManyRelatedManager'):
- return [self.to_native(item) for item in obj.all()]
- return self.to_native(obj)
-
- def attributes(self):
- try:
- return {
- "rel": self.model_field.rel.__class__.__name__,
- "to": smart_unicode(self.model_field.rel.to._meta)
- }
- except AttributeError:
- return {}
-
-
-class PrimaryKeyRelatedField(RelatedField):
- """
- Serializes a model related field or related manager to a pk value.
- """
-
- # Note the we use ModelRelatedField's implementation, as we want to get the
- # raw database value directly, since that won't involve another
- # database lookup.
- #
- # An alternative implementation would simply be this...
- #
- # class PrimaryKeyRelatedField(RelatedField):
- # def to_native(self, obj):
- # return obj.pk
-
- def to_native(self, pk):
- """
- Simply returns the object's pk. You can subclass this method to
- provide different serialization behavior of the pk.
- (For example returning a URL based on the model's pk.)
- """
- return pk
-
- def field_to_native(self, obj, field_name):
- try:
- obj = obj.serializable_value(field_name)
- except AttributeError:
- field = obj._meta.get_field_by_name(field_name)[0]
- obj = getattr(obj, field_name)
- if obj.__class__.__name__ == 'RelatedManager':
- return [self.to_native(item.pk) for item in obj.all()]
- elif isinstance(field, RelatedObject):
- return self.to_native(obj.pk)
- raise
- if obj.__class__.__name__ == 'ManyRelatedManager':
- return [self.to_native(item.pk) for item in obj.all()]
- return self.to_native(obj)
-
- def field_from_native(self, data, field_name, into):
- value = data.get(field_name)
- if hasattr(value, '__iter__'):
- into[field_name] = [self.from_native(item) for item in value]
- else:
- into[field_name + '_id'] = self.from_native(value)
-
-
-class NaturalKeyRelatedField(RelatedField):
- """
- Serializes a model related field or related manager to a natural key value.
- """
- is_natural_key = True # XML renderer handles these differently
-
- def to_native(self, obj):
- if hasattr(obj, 'natural_key'):
- return obj.natural_key()
- return obj
-
- def field_from_native(self, data, field_name, into):
- value = data.get(field_name)
- into[self.model_field.attname] = self.from_native(value)
-
- def from_native(self, value):
- # TODO: Support 'using' : db = options.pop('using', DEFAULT_DB_ALIAS)
- manager = self.model_field.rel.to._default_manager
- manager = manager.db_manager(DEFAULT_DB_ALIAS)
- return manager.get_by_natural_key(*value).pk
-
-
-class BooleanField(Field):
- default_error_messages = {
- 'invalid': _(u"'%s' value must be either True or False."),
- }
-
- def from_native(self, value):
- if value in (True, False):
- # if value is 1 or 0 than it's equal to True or False, but we want
- # to return a true bool for semantic reasons.
- return bool(value)
- if value in ('t', 'True', '1'):
- return True
- if value in ('f', 'False', '0'):
- return False
- raise ValidationError(self.error_messages['invalid'] % value)
-
-
-class CharField(Field):
- def __init__(self, max_length=None, min_length=None, *args, **kwargs):
- self.max_length, self.min_length = max_length, min_length
- super(CharField, self).__init__(*args, **kwargs)
- if min_length is not None:
- self.validators.append(validators.MinLengthValidator(min_length))
- if max_length is not None:
- self.validators.append(validators.MaxLengthValidator(max_length))
-
- def from_native(self, value):
- if isinstance(value, basestring) or value is None:
- return value
- return smart_unicode(value)
-
-
-class EmailField(CharField):
- default_error_messages = {
- 'invalid': _('Enter a valid e-mail address.'),
- }
- default_validators = [validators.validate_email]
-
- def from_native(self, value):
- return super(EmailField, self).from_native(value).strip()
-
- def __deepcopy__(self, memo):
- result = copy.copy(self)
- memo[id(self)] = result
- #result.widget = copy.deepcopy(self.widget, memo)
- result.validators = self.validators[:]
- return result
-
-
-class DateField(Field):
- default_error_messages = {
- 'invalid': _(u"'%s' value has an invalid date format. It must be "
- u"in YYYY-MM-DD format."),
- 'invalid_date': _(u"'%s' value has the correct format (YYYY-MM-DD) "
- u"but it is an invalid date."),
- }
- empty = None
-
- def from_native(self, value):
- if value is None:
- return value
- if isinstance(value, datetime.datetime):
- if timezone and settings.USE_TZ and timezone.is_aware(value):
- # Convert aware datetimes to the default time zone
- # before casting them to dates (#17742).
- default_timezone = timezone.get_default_timezone()
- value = timezone.make_naive(value, default_timezone)
- return value.date()
- if isinstance(value, datetime.date):
- return value
-
- try:
- parsed = parse_date(value)
- if parsed is not None:
- return parsed
- except ValueError:
- msg = self.error_messages['invalid_date'] % value
- raise ValidationError(msg)
-
- msg = self.error_messages['invalid'] % value
- raise ValidationError(msg)
-
-
-class DateTimeField(Field):
- default_error_messages = {
- 'invalid': _(u"'%s' value has an invalid format. It must be in "
- u"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."),
- 'invalid_date': _(u"'%s' value has the correct format "
- u"(YYYY-MM-DD) but it is an invalid date."),
- 'invalid_datetime': _(u"'%s' value has the correct format "
- u"(YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]) "
- u"but it is an invalid date/time."),
- }
- empty = None
-
- def from_native(self, value):
- if value is None:
- return value
- if isinstance(value, datetime.datetime):
- return value
- if isinstance(value, datetime.date):
- value = datetime.datetime(value.year, value.month, value.day)
- if settings.USE_TZ:
- # For backwards compatibility, interpret naive datetimes in
- # local time. This won't work during DST change, but we can't
- # do much about it, so we let the exceptions percolate up the
- # call stack.
- warnings.warn(u"DateTimeField received a naive datetime (%s)"
- u" while time zone support is active." % value,
- RuntimeWarning)
- default_timezone = timezone.get_default_timezone()
- value = timezone.make_aware(value, default_timezone)
- return value
-
- try:
- parsed = parse_datetime(value)
- if parsed is not None:
- return parsed
- except ValueError:
- msg = self.error_messages['invalid_datetime'] % value
- raise ValidationError(msg)
-
- try:
- parsed = parse_date(value)
- if parsed is not None:
- return datetime.datetime(parsed.year, parsed.month, parsed.day)
- except ValueError:
- msg = self.error_messages['invalid_date'] % value
- raise ValidationError(msg)
-
- msg = self.error_messages['invalid'] % value
- raise ValidationError(msg)
-
-
-class IntegerField(Field):
- default_error_messages = {
- 'invalid': _('Enter a whole number.'),
- 'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
- 'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
- }
-
- def __init__(self, max_value=None, min_value=None, *args, **kwargs):
- self.max_value, self.min_value = max_value, min_value
- super(IntegerField, self).__init__(*args, **kwargs)
-
- if max_value is not None:
- self.validators.append(validators.MaxValueValidator(max_value))
- if min_value is not None:
- self.validators.append(validators.MinValueValidator(min_value))
-
- def from_native(self, value):
- if value in validators.EMPTY_VALUES:
- return None
- try:
- value = int(str(value))
- except (ValueError, TypeError):
- raise ValidationError(self.error_messages['invalid'])
- return value
-
-
-class FloatField(Field):
- default_error_messages = {
- 'invalid': _("'%s' value must be a float."),
- }
-
- def from_native(self, value):
- if value is None:
- return value
- try:
- return float(value)
- except (TypeError, ValueError):
- msg = self.error_messages['invalid'] % value
- raise ValidationError(msg)
-
-# field_mapping = {
-# models.AutoField: IntegerField,
-# models.BooleanField: BooleanField,
-# models.CharField: CharField,
-# models.DateTimeField: DateTimeField,
-# models.DateField: DateField,
-# models.BigIntegerField: IntegerField,
-# models.IntegerField: IntegerField,
-# models.PositiveIntegerField: IntegerField,
-# models.FloatField: FloatField
-# }
-
-
-# def modelfield_to_serializerfield(field):
-# return field_mapping.get(type(field), Field)
diff --git a/djangorestframework/generics.py b/djangorestframework/generics.py
deleted file mode 100644
index 5815b73a..00000000
--- a/djangorestframework/generics.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""
-Generic views that provide commmonly needed behaviour.
-"""
-
-from djangorestframework import views, mixins
-from django.views.generic.detail import SingleObjectMixin
-from django.views.generic.list import MultipleObjectMixin
-
-
-### Base classes for the generic views ###
-
-class BaseView(views.APIView):
- """
- Base class for all other generic views.
- """
- serializer_class = None
-
- def get_serializer(self, data=None, files=None, instance=None):
- # TODO: add support for files
- # TODO: add support for seperate serializer/deserializer
- context = {
- 'request': self.request,
- 'format': self.kwargs.get('format', None)
- }
- return self.serializer_class(data, instance=instance, context=context)
-
-
-class MultipleObjectBaseView(MultipleObjectMixin, BaseView):
- """
- Base class for generic views onto a queryset.
- """
- pass
-
-
-class SingleObjectBaseView(SingleObjectMixin, BaseView):
- """
- Base class for generic views onto a model instance.
- """
-
- def get_object(self):
- """
- Override default to add support for object-level permissions.
- """
- obj = super(SingleObjectBaseView, self).get_object()
- self.check_permissions(self.request, obj)
- return obj
-
-
-### Concrete view classes that provide method handlers ###
-### by composing the mixin classes with a base view. ###
-
-class ListAPIView(mixins.ListModelMixin,
- mixins.MetadataMixin,
- MultipleObjectBaseView):
- """
- Concrete view for listing a queryset.
- """
- def get(self, request, *args, **kwargs):
- return self.list(request, *args, **kwargs)
-
- def options(self, request, *args, **kwargs):
- return self.metadata(request, *args, **kwargs)
-
-
-class RootAPIView(mixins.ListModelMixin,
- mixins.CreateModelMixin,
- mixins.MetadataMixin,
- MultipleObjectBaseView):
- """
- Concrete view for listing a queryset or creating a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.list(request, *args, **kwargs)
-
- def post(self, request, *args, **kwargs):
- return self.create(request, *args, **kwargs)
-
- def options(self, request, *args, **kwargs):
- return self.metadata(request, *args, **kwargs)
-
-
-class DetailAPIView(mixins.RetrieveModelMixin,
- mixins.MetadataMixin,
- SingleObjectBaseView):
- """
- Concrete view for retrieving a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.retrieve(request, *args, **kwargs)
-
- def options(self, request, *args, **kwargs):
- return self.metadata(request, *args, **kwargs)
-
-
-class InstanceAPIView(mixins.RetrieveModelMixin,
- mixins.UpdateModelMixin,
- mixins.DestroyModelMixin,
- mixins.MetadataMixin,
- SingleObjectBaseView):
- """
- Concrete view for retrieving, updating or deleting a model instance.
- """
- def get(self, request, *args, **kwargs):
- return self.retrieve(request, *args, **kwargs)
-
- def put(self, request, *args, **kwargs):
- return self.update(request, *args, **kwargs)
-
- def delete(self, request, *args, **kwargs):
- return self.destroy(request, *args, **kwargs)
-
- def options(self, request, *args, **kwargs):
- return self.metadata(request, *args, **kwargs)
diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py
deleted file mode 100644
index 1f06dd34..00000000
--- a/djangorestframework/mixins.py
+++ /dev/null
@@ -1,97 +0,0 @@
-"""
-Basic building blocks for generic class based views.
-
-We don't bind behaviour to http method handlers yet,
-which allows mixin classes to be composed in interesting ways.
-
-Eg. Use mixins to build a Resource class, and have a Router class
- perform the binding of http methods to actions for us.
-"""
-from djangorestframework import status
-from djangorestframework.response import Response
-
-
-class CreateModelMixin(object):
- """
- Create a model instance.
- Should be mixed in with any `BaseView`.
- """
- def create(self, request, *args, **kwargs):
- serializer = self.get_serializer(data=request.DATA)
- if serializer.is_valid():
- self.object = serializer.object
- self.object.save()
- return Response(serializer.data, status=status.HTTP_201_CREATED)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
-
-
-class ListModelMixin(object):
- """
- List a queryset.
- Should be mixed in with `MultipleObjectBaseView`.
- """
- def list(self, request, *args, **kwargs):
- self.object_list = self.get_queryset()
- serializer = self.get_serializer(instance=self.object_list)
- return Response(serializer.data)
-
-
-class RetrieveModelMixin(object):
- """
- Retrieve a model instance.
- Should be mixed in with `SingleObjectBaseView`.
- """
- def retrieve(self, request, *args, **kwargs):
- self.object = self.get_object()
- serializer = self.get_serializer(instance=self.object)
- return Response(serializer.data)
-
-
-class UpdateModelMixin(object):
- """
- Update a model instance.
- Should be mixed in with `SingleObjectBaseView`.
- """
- def update(self, request, *args, **kwargs):
- self.object = self.get_object()
- serializer = self.get_serializer(data=request.DATA, instance=self.object)
- if serializer.is_valid():
- self.object = serializer.object
- self.object.save()
- return Response(serializer.data)
- return Response(serializer.error_data, status=status.HTTP_400_BAD_REQUEST)
-
-
-class DestroyModelMixin(object):
- """
- Destroy a model instance.
- Should be mixed in with `SingleObjectBaseView`.
- """
- def destroy(self, request, *args, **kwargs):
- self.object = self.get_object()
- self.object.delete()
- return Response(status=status.HTTP_204_NO_CONTENT)
-
-
-class MetadataMixin(object):
- """
- Return a dicitonary of view metadata.
- Should be mixed in with any `BaseView`.
-
- This mixin is typically used for the HTTP 'OPTIONS' method.
- """
- def metadata(self, request, *args, **kwargs):
- content = {
- 'name': self.get_name(),
- 'description': self.get_description(),
- 'renders': self._rendered_media_types,
- 'parses': self._parsed_media_types,
- }
- # TODO: Add 'fields', from serializer info.
- # form = self.get_bound_form()
- # if form is not None:
- # field_name_types = {}
- # for name, field in form.fields.iteritems():
- # field_name_types[name] = field.__class__.__name__
- # content['fields'] = field_name_types
- return Response(content, status=status.HTTP_200_OK)
diff --git a/djangorestframework/models.py b/djangorestframework/models.py
deleted file mode 100644
index 5b53a526..00000000
--- a/djangorestframework/models.py
+++ /dev/null
@@ -1 +0,0 @@
-# Just to keep things like ./manage.py test happy
diff --git a/djangorestframework/parsers.py b/djangorestframework/parsers.py
deleted file mode 100644
index fb08c5a0..00000000
--- a/djangorestframework/parsers.py
+++ /dev/null
@@ -1,260 +0,0 @@
-"""
-Django supports parsing the content of an HTTP request, but only for form POST requests.
-That behavior is sufficient for dealing with standard HTML forms, but it doesn't map well
-to general HTTP requests.
-
-We need a method to be able to:
-
-1.) Determine the parsed content on a request for methods other than POST (eg typically also PUT)
-
-2.) Determine the parsed content on a request for media types other than application/x-www-form-urlencoded
- and multipart/form-data. (eg also handle multipart/json)
-"""
-
-from django.http import QueryDict
-from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
-from django.http.multipartparser import MultiPartParserError
-from django.utils import simplejson as json
-from djangorestframework.compat import yaml
-from djangorestframework.exceptions import ParseError
-from djangorestframework.utils.mediatypes import media_type_matches
-from xml.etree import ElementTree as ET
-from djangorestframework.compat import ETParseError
-from xml.parsers.expat import ExpatError
-import datetime
-import decimal
-from io import BytesIO
-
-
-__all__ = (
- 'BaseParser',
- 'JSONParser',
- 'PlainTextParser',
- 'FormParser',
- 'MultiPartParser',
- 'YAMLParser',
- 'XMLParser'
-)
-
-
-class DataAndFiles(object):
- def __init__(self, data, files):
- self.data = data
- self.files = files
-
-
-class BaseParser(object):
- """
- All parsers should extend :class:`BaseParser`, specifying a :attr:`media_type` attribute,
- and overriding the :meth:`parse` method.
- """
-
- media_type = None
-
- 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.
- """
- return media_type_matches(self.media_type, content_type)
-
- def parse(self, string_or_stream, **opts):
- """
- The main entry point to parsers. This is a light wrapper around
- `parse_stream`, that instead handles both string and stream objects.
- """
- if isinstance(string_or_stream, basestring):
- stream = BytesIO(string_or_stream)
- else:
- stream = string_or_stream
- return self.parse_stream(stream, **opts)
-
- def parse_stream(self, stream, **opts):
- """
- Given a *stream* to read from, return the deserialized output.
- Should return parsed data, or a DataAndFiles object consisting of the
- parsed data and files.
- """
- raise NotImplementedError(".parse_stream() must be overridden.")
-
-
-class JSONParser(BaseParser):
- """
- Parses JSON-serialized data.
- """
-
- media_type = 'application/json'
-
- def parse_stream(self, stream, **opts):
- """
- 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`.
- """
- try:
- return json.load(stream)
- except ValueError, exc:
- raise ParseError('JSON parse error - %s' % unicode(exc))
-
-
-class YAMLParser(BaseParser):
- """
- Parses YAML-serialized data.
- """
-
- media_type = 'application/yaml'
-
- def parse_stream(self, stream, **opts):
- """
- 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`.
- """
- try:
- return yaml.safe_load(stream)
- except (ValueError, yaml.parser.ParserError), exc:
- raise ParseError('YAML parse error - %s' % unicode(exc))
-
-
-class PlainTextParser(BaseParser):
- """
- Plain text parser.
- """
-
- media_type = 'text/plain'
-
- def parse_stream(self, stream, **opts):
- """
- Returns a 2-tuple of `(data, files)`.
-
- `data` will simply be a string representing the body of the request.
- `files` will always be `None`.
- """
- return stream.read()
-
-
-class FormParser(BaseParser):
- """
- Parser for form data.
- """
-
- media_type = 'application/x-www-form-urlencoded'
-
- def parse_stream(self, stream, **opts):
- """
- Returns a 2-tuple of `(data, files)`.
-
- `data` will be a :class:`QueryDict` containing all the form parameters.
- `files` will always be :const:`None`.
- """
- data = QueryDict(stream.read())
- return data
-
-
-class MultiPartParser(BaseParser):
- """
- Parser for multipart form data, which may include file data.
- """
-
- media_type = 'multipart/form-data'
-
- def parse_stream(self, stream, **opts):
- """
- Returns a DataAndFiles object.
-
- `.data` will be a `QueryDict` containing all the form parameters.
- `.files` will be a `QueryDict` containing all the form files.
- """
- meta = opts['meta']
- upload_handlers = opts['upload_handlers']
- try:
- parser = DjangoMultiPartParser(meta, stream, upload_handlers)
- data, files = parser.parse()
- return DataAndFiles(data, files)
- except MultiPartParserError, exc:
- raise ParseError('Multipart form parse error - %s' % unicode(exc))
-
-
-class XMLParser(BaseParser):
- """
- XML parser.
- """
-
- media_type = 'application/xml'
-
- def parse_stream(self, stream, **opts):
- try:
- tree = ET.parse(stream)
- except (ExpatError, ETParseError, ValueError), exc:
- raise ParseError('XML parse error - %s' % unicode(exc))
- data = self._xml_convert(tree.getroot())
-
- return data
-
- def _xml_convert(self, element):
- """
- convert the xml `element` into the corresponding python object
- """
-
- children = element.getchildren()
-
- 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
-
-
-DEFAULT_PARSERS = (
- JSONParser,
- FormParser,
- MultiPartParser,
- XMLParser
-)
-
-if yaml:
- DEFAULT_PARSERS += (YAMLParser, )
-else:
- YAMLParser = None
diff --git a/djangorestframework/permissions.py b/djangorestframework/permissions.py
deleted file mode 100644
index 3a669822..00000000
--- a/djangorestframework/permissions.py
+++ /dev/null
@@ -1,116 +0,0 @@
-"""
-The :mod:`permissions` module bundles a set of permission classes that are used
-for checking if a request passes a certain set of constraints.
-
-Permission behavior is provided by mixing the :class:`mixins.PermissionsMixin` class into a :class:`View` class.
-"""
-
-__all__ = (
- 'BasePermission',
- 'FullAnonAccess',
- 'IsAuthenticated',
- 'IsAdminUser',
- 'IsUserOrIsAnonReadOnly',
- 'PerUserThrottling',
- 'PerViewThrottling',
-)
-
-SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
-
-
-class BasePermission(object):
- """
- A base class from which all permission classes should inherit.
- """
- def __init__(self, view):
- """
- Permission classes are always passed the current view on creation.
- """
- self.view = view
-
- def has_permission(self, request, obj=None):
- """
- Should simply return, or raise an :exc:`response.ImmediateResponse`.
- """
- raise NotImplementedError(".has_permission() must be overridden.")
-
-
-class IsAuthenticated(BasePermission):
- """
- Allows access only to authenticated users.
- """
-
- def has_permission(self, request, obj=None):
- if request.user and request.user.is_authenticated():
- return True
- return False
-
-
-class IsAdminUser(BasePermission):
- """
- Allows access only to admin users.
- """
-
- def has_permission(self, request, obj=None):
- if request.user and request.user.is_staff:
- return True
- return False
-
-
-class IsAuthenticatedOrReadOnly(BasePermission):
- """
- The request is authenticated as a user, or is a read-only request.
- """
-
- def has_permission(self, request, obj=None):
- if (request.method in SAFE_METHODS or
- request.user and
- request.user.is_authenticated()):
- return True
- return False
-
-
-class DjangoModelPermissions(BasePermission):
- """
- The request is authenticated using `django.contrib.auth` permissions.
- See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
-
- It ensures that the user is authenticated, and has the appropriate
- `add`/`change`/`delete` permissions on the model.
-
- This permission should only be used on views with a `ModelResource`.
- """
-
- # Map methods into required permission codes.
- # Override this if you need to also provide 'view' permissions,
- # or if you want to provide custom permission codes.
- perms_map = {
- 'GET': [],
- 'OPTIONS': [],
- 'HEAD': [],
- 'POST': ['%(app_label)s.add_%(model_name)s'],
- 'PUT': ['%(app_label)s.change_%(model_name)s'],
- 'PATCH': ['%(app_label)s.change_%(model_name)s'],
- 'DELETE': ['%(app_label)s.delete_%(model_name)s'],
- }
-
- def get_required_permissions(self, method, model_cls):
- """
- Given a model and an HTTP method, return the list of permission
- codes that the user is required to have.
- """
- kwargs = {
- 'app_label': model_cls._meta.app_label,
- 'model_name': model_cls._meta.module_name
- }
- return [perm % kwargs for perm in self.perms_map[method]]
-
- def has_permission(self, request, obj=None):
- model_cls = self.view.model
- perms = self.get_required_permissions(request.method, model_cls)
-
- if (request.user and
- request.user.is_authenticated() and
- request.user.has_perms(perms, obj)):
- return True
- return False
diff --git a/djangorestframework/renderers.py b/djangorestframework/renderers.py
deleted file mode 100644
index 26e8cba1..00000000
--- a/djangorestframework/renderers.py
+++ /dev/null
@@ -1,408 +0,0 @@
-"""
-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.
-"""
-from django import forms
-from django.template import RequestContext, loader
-from django.utils import simplejson as json
-
-from djangorestframework.compat import yaml
-from djangorestframework.settings import api_settings
-from djangorestframework.utils import dict2xml
-from djangorestframework.utils import encoders
-from djangorestframework.utils.breadcrumbs import get_breadcrumbs
-from djangorestframework.utils.mediatypes import get_media_type_params, add_media_type_param, media_type_matches
-from djangorestframework import VERSION
-from djangorestframework.fields import FloatField, IntegerField, DateTimeField, DateField, EmailField, CharField, BooleanField
-
-import string
-
-
-__all__ = (
- 'BaseRenderer',
- 'TemplateRenderer',
- 'JSONRenderer',
- 'JSONPRenderer',
- 'DocumentingHTMLRenderer',
- 'DocumentingXHTMLRenderer',
- 'DocumentingPlainTextRenderer',
- 'XMLRenderer',
- 'YAMLRenderer'
-)
-
-
-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
- format = None
-
- def __init__(self, view=None):
- self.view = view
-
- def can_handle_response(self, accept):
- """
- Returns :const:`True` if this renderer is able to deal with the given
- *accept* media type.
-
- The default implementation for this function is to check the *accept*
- 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.
- """
- # TODO: format overriding must go out of here
- format = None
- if self.view is not None:
- format = self.view.kwargs.get(self._FORMAT_QUERY_PARAM, None)
- if format is None and self.view is not None:
- format = self.view.request.GET.get(self._FORMAT_QUERY_PARAM, None)
-
- if format is not None:
- return format == self.format
- return media_type_matches(self.media_type, accept)
-
- def render(self, obj=None, media_type=None):
- """
- Given an object render it into a string.
-
- The requested media type is also passed to this method,
- as it may contain parameters relevant to how the parser
- should render the output.
- EG: ``application/json; indent=4``
-
- By default render simply returns the output as-is.
- Override this method to provide for other behavior.
- """
- if obj is None:
- return ''
-
- return str(obj)
-
-
-class JSONRenderer(BaseRenderer):
- """
- Renderer which serializes to JSON
- """
-
- media_type = 'application/json'
- format = 'json'
- encoder_class = encoders.JSONEncoder
-
- def render(self, obj=None, media_type=None):
- """
- Renders *obj* into serialized JSON.
- """
- if obj is None:
- return ''
-
- # If the media type looks like 'application/json; indent=4', then
- # pretty print the result.
- indent = get_media_type_params(media_type).get('indent', None)
- sort_keys = False
- try:
- indent = max(min(int(indent), 8), 0)
- sort_keys = True
- except (ValueError, TypeError):
- indent = None
-
- return json.dumps(obj, cls=self.encoder_class, indent=indent, sort_keys=sort_keys)
-
-
-class JSONPRenderer(JSONRenderer):
- """
- Renderer which serializes to JSONP
- """
-
- media_type = 'application/javascript'
- format = 'jsonp'
- renderer_class = JSONRenderer
- callback_parameter = 'callback'
-
- def _get_callback(self):
- return self.view.request.GET.get(self.callback_parameter, self.callback_parameter)
-
- def _get_renderer(self):
- return self.renderer_class(self.view)
-
- def render(self, obj=None, media_type=None):
- callback = self._get_callback()
- json = self._get_renderer().render(obj, media_type)
- return "%s(%s);" % (callback, json)
-
-
-class XMLRenderer(BaseRenderer):
- """
- Renderer which serializes to XML.
- """
-
- media_type = 'application/xml'
- format = 'xml'
-
- def render(self, obj=None, media_type=None):
- """
- Renders *obj* into serialized XML.
- """
- if obj is None:
- return ''
- return dict2xml(obj)
-
-
-class YAMLRenderer(BaseRenderer):
- """
- Renderer which serializes to YAML.
- """
-
- media_type = 'application/yaml'
- format = 'yaml'
-
- def render(self, obj=None, media_type=None):
- """
- Renders *obj* into serialized YAML.
- """
- if obj is None:
- return ''
-
- return yaml.safe_dump(obj)
-
-
-class TemplateRenderer(BaseRenderer):
- """
- A Base class provided for convenience.
-
- Render the object simply by using the given template.
- To create a template renderer, subclass this class, and set
- the :attr:`media_type` and :attr:`template` attributes.
- """
-
- media_type = None
- template = None
-
- def render(self, obj=None, media_type=None):
- """
- Renders *obj* using the :attr:`template` specified on the class.
- """
- if obj is None:
- return ''
-
- template = loader.get_template(self.template)
- context = RequestContext(self.view.request, {'object': obj})
- return template.render(context)
-
-
-class DocumentingTemplateRenderer(BaseRenderer):
- """
- Base class for renderers used to self-document the API.
- Implementing classes should extend this class and set the template attribute.
- """
-
- template = None
-
- def _get_content(self, view, request, obj, media_type):
- """
- Get the content as if it had been rendered by a non-documenting renderer.
-
- (Typically this will be the content as it would have been if the Resource had been
- requested with an 'Accept: */*' header, although with verbose style formatting if appropriate.)
- """
-
- # Find the first valid renderer and render the content. (Don't use another documenting renderer.)
- renderers = [renderer for renderer in view.renderer_classes
- if not issubclass(renderer, DocumentingTemplateRenderer)]
- if not renderers:
- return '[No renderers were found]'
-
- media_type = add_media_type_param(media_type, 'indent', '4')
- 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
-
- def _get_form_instance(self, view, method):
- """
- Get a form, possibly bound to either the input or output data.
- In the absence on of the Resource having an associated form then
- provide a form that can be used to submit arbitrary content.
- """
- if not hasattr(self.view, 'get_serializer'): # No serializer, no form.
- return
- # We need to map our Fields to Django's Fields.
- field_mapping = dict([
- [FloatField.__name__, forms.FloatField],
- [IntegerField.__name__, forms.IntegerField],
- [DateTimeField.__name__, forms.DateTimeField],
- [DateField.__name__, forms.DateField],
- [EmailField.__name__, forms.EmailField],
- [CharField.__name__, forms.CharField],
- [BooleanField.__name__, forms.BooleanField]
- ])
-
- # Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
- fields = {}
- object, data = None, None
- if hasattr(self.view, 'object'):
- object = self.view.object
- serializer = self.view.get_serializer(instance=object)
- for k, v in serializer.fields.items():
- fields[k] = field_mapping[v.__class__.__name__]()
- OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields)
- if object and not self.view.request.method == 'DELETE': # Don't fill in the form when the object is deleted
- data = serializer.data
- form_instance = OnTheFlyForm(data)
- return form_instance
-
- def _get_generic_content_form(self, view):
- """
- Returns a form that allows for arbitrary content types to be tunneled via standard HTML forms
- (Which are typically application/x-www-form-urlencoded)
- """
-
- # If we're not using content overloading there's no point in supplying a generic form,
- # as the view won't treat the form's value as the content of the request.
- if not getattr(view.request, '_USE_FORM_OVERLOADING', False):
- return None
-
- # NB. http://jacobian.org/writing/dynamic-form-generation/
- class GenericContentForm(forms.Form):
- def __init__(self, view, request):
- """We don't know the names of the fields we want to set until the point the form is instantiated,
- as they are determined by the Resource the form is being created against.
- Add the fields dynamically."""
- super(GenericContentForm, self).__init__()
-
- contenttype_choices = [(media_type, media_type) for media_type in view._parsed_media_types]
- initial_contenttype = view._default_parser.media_type
-
- self.fields[request._CONTENTTYPE_PARAM] = forms.ChoiceField(label='Content Type',
- choices=contenttype_choices,
- initial=initial_contenttype)
- self.fields[request._CONTENT_PARAM] = forms.CharField(label='Content',
- widget=forms.Textarea)
-
- # If either of these reserved parameters are turned off then content tunneling is not possible
- if self.view.request._CONTENTTYPE_PARAM is None or self.view.request._CONTENT_PARAM is None:
- return None
-
- # Okey doke, let's do it
- return GenericContentForm(view, view.request)
-
- def get_name(self):
- try:
- return self.view.get_name()
- except AttributeError:
- return self.view.__doc__
-
- def get_description(self, html=None):
- if html is None:
- html = bool('html' in self.format)
- try:
- return self.view.get_description(html)
- except AttributeError:
- return self.view.__doc__
-
- def render(self, obj=None, media_type=None):
- """
- Renders *obj* using the :attr:`template` set on the class.
-
- The context used in the template contains all the information
- needed to self-document the response to this request.
- """
-
- content = self._get_content(self.view, self.view.request, obj, media_type)
-
- put_form_instance = self._get_form_instance(self.view, 'put')
- post_form_instance = self._get_form_instance(self.view, 'post')
-
- name = self.get_name()
- description = self.get_description()
-
- breadcrumb_list = get_breadcrumbs(self.view.request.path)
-
- template = loader.get_template(self.template)
- context = RequestContext(self.view.request, {
- 'content': content,
- 'view': self.view,
- 'request': self.view.request,
- 'response': self.view.response,
- 'description': description,
- 'name': name,
- 'version': VERSION,
- 'breadcrumblist': breadcrumb_list,
- 'allowed_methods': self.view.allowed_methods,
- 'available_formats': self.view._rendered_formats,
- 'put_form': put_form_instance,
- 'post_form': post_form_instance,
- 'FORMAT_PARAM': self._FORMAT_QUERY_PARAM,
- 'METHOD_PARAM': getattr(self.view, '_METHOD_PARAM', None),
- 'api_settings': api_settings
- })
-
- ret = template.render(context)
-
- # Munge DELETE Response code to allow us to return content
- # (Do this *after* we've rendered the template so that we include
- # the normal deletion response code in the output)
- if self.view.response.status_code == 204:
- self.view.response.status_code = 200
-
- return ret
-
-
-class DocumentingHTMLRenderer(DocumentingTemplateRenderer):
- """
- Renderer which provides a browsable HTML interface for an API.
- See the examples at http://api.django-rest-framework.org to see this in action.
- """
-
- media_type = 'text/html'
- format = 'html'
- template = 'djangorestframework/api.html'
-
-
-class DocumentingXHTMLRenderer(DocumentingTemplateRenderer):
- """
- Identical to DocumentingHTMLRenderer, except with an xhtml media type.
- We need this to be listed in preference to xml in order to return HTML to WebKit based browsers,
- given their Accept headers.
- """
-
- media_type = 'application/xhtml+xml'
- format = 'xhtml'
- template = 'djangorestframework/api.html'
-
-
-class DocumentingPlainTextRenderer(DocumentingTemplateRenderer):
- """
- Renderer that serializes the object with the default renderer, but also provides plain-text
- documentation of the returned status and headers, and of the resource's name and description.
- Useful for browsing an API with command line tools.
- """
-
- media_type = 'text/plain'
- format = 'txt'
- template = 'djangorestframework/api.txt'
-
-
-DEFAULT_RENDERERS = (
- JSONRenderer,
- JSONPRenderer,
- DocumentingHTMLRenderer,
- DocumentingXHTMLRenderer,
- DocumentingPlainTextRenderer,
- XMLRenderer
-)
-
-if yaml:
- DEFAULT_RENDERERS += (YAMLRenderer, )
-else:
- YAMLRenderer = None
diff --git a/djangorestframework/request.py b/djangorestframework/request.py
deleted file mode 100644
index 450d2ac7..00000000
--- a/djangorestframework/request.py
+++ /dev/null
@@ -1,284 +0,0 @@
-"""
-The :mod:`request` module provides a :class:`Request` class used to wrap the standard `request`
-object received in all the views.
-
-The wrapped request then offers a richer API, in particular :
-
- - content automatically parsed according to `Content-Type` header,
- and available as :meth:`.DATA<Request.DATA>`
- - full support of PUT method, including support for file uploads
- - form overloading of HTTP method, content type and content
-"""
-from StringIO import StringIO
-
-from djangorestframework import exceptions
-from djangorestframework.settings import api_settings
-from djangorestframework.utils.mediatypes import is_form_media_type
-
-
-__all__ = ('Request',)
-
-
-class Empty(object):
- """
- Placeholder for unset attributes.
- Cannot use `None`, as that may be a valid value.
- """
- pass
-
-
-def _hasattr(obj, name):
- return not getattr(obj, name) is Empty
-
-
-class Request(object):
- """
- Wrapper allowing to enhance a standard `HttpRequest` instance.
-
- Kwargs:
- - request(HttpRequest). The original request instance.
- - parsers_classes(list/tuple). The parsers to use for parsing the
- request content.
- - authentication_classes(list/tuple). The authentications used to try
- authenticating the request's user.
- """
-
- _METHOD_PARAM = api_settings.FORM_METHOD_OVERRIDE
- _CONTENT_PARAM = api_settings.FORM_CONTENT_OVERRIDE
- _CONTENTTYPE_PARAM = api_settings.FORM_CONTENTTYPE_OVERRIDE
-
- def __init__(self, request, parser_classes=None, authentication_classes=None):
- self._request = request
- self.parser_classes = parser_classes or ()
- self.authentication_classes = authentication_classes or ()
- self._data = Empty
- self._files = Empty
- self._method = Empty
- self._content_type = Empty
- self._stream = Empty
-
- def get_parsers(self):
- """
- Instantiates and returns the list of parsers the request will use.
- """
- return [parser() for parser in self.parser_classes]
-
- def get_authentications(self):
- """
- Instantiates and returns the list of parsers the request will use.
- """
- return [authentication() for authentication in self.authentication_classes]
-
- @property
- def method(self):
- """
- Returns the HTTP method.
-
- This allows the `method` to be overridden by using a hidden `form`
- field on a form POST request.
- """
- if not _hasattr(self, '_method'):
- self._load_method_and_content_type()
- return self._method
-
- @property
- def content_type(self):
- """
- Returns the content type header.
-
- This should be used instead of ``request.META.get('HTTP_CONTENT_TYPE')``,
- as it allows the content type to be overridden by using a hidden form
- field on a form POST request.
- """
- if not _hasattr(self, '_content_type'):
- self._load_method_and_content_type()
- return self._content_type
-
- @property
- def stream(self):
- """
- Returns an object that may be used to stream the request content.
- """
- if not _hasattr(self, '_stream'):
- self._load_stream()
- return self._stream
-
- @property
- def DATA(self):
- """
- Parses the request body and returns the data.
-
- Similar to usual behaviour of `request.POST`, except that it handles
- arbitrary parsers, and also works on methods other than POST (eg PUT).
- """
- if not _hasattr(self, '_data'):
- self._load_data_and_files()
- return self._data
-
- @property
- def FILES(self):
- """
- Parses the request body and returns any files uploaded in the request.
-
- Similar to usual behaviour of `request.FILES`, except that it handles
- arbitrary parsers, and also works on methods other than POST (eg PUT).
- """
- if not _hasattr(self, '_files'):
- self._load_data_and_files()
- return self._files
-
- @property
- def user(self):
- """
- Returns the user associated with the current request, as authenticated
- by the authentication classes provided to the request.
- """
- if not hasattr(self, '_user'):
- self._user, self._auth = self._authenticate()
- return self._user
-
- @property
- def auth(self):
- """
- Returns any non-user authentication information associated with the
- request, such as an authentication token.
- """
- if not hasattr(self, '_auth'):
- self._user, self._auth = self._authenticate()
- return self._auth
-
- def _load_data_and_files(self):
- """
- Parses the request content into self.DATA and self.FILES.
- """
- if not _hasattr(self, '_content_type'):
- self._load_method_and_content_type()
-
- if not _hasattr(self, '_data'):
- self._data, self._files = self._parse()
-
- def _load_method_and_content_type(self):
- """
- Sets the method and content_type, and then check if they've
- been overridden.
- """
- self._content_type = self.META.get('HTTP_CONTENT_TYPE',
- self.META.get('CONTENT_TYPE', ''))
- self._perform_form_overloading()
- # if the HTTP method was not overloaded, we take the raw HTTP method
- if not _hasattr(self, '_method'):
- self._method = self._request.method
-
- def _load_stream(self):
- """
- Return the content body of the request, as a stream.
- """
- try:
- content_length = int(self.META.get('CONTENT_LENGTH',
- self.META.get('HTTP_CONTENT_LENGTH')))
- except (ValueError, TypeError):
- content_length = 0
-
- if content_length == 0:
- self._stream = None
- elif hasattr(self._request, 'read'):
- self._stream = self._request
- else:
- self._stream = StringIO(self.raw_post_data)
-
- def _perform_form_overloading(self):
- """
- If this is a form POST request, then we need to check if the method and
- content/content_type have been overridden by setting them in hidden
- form fields or not.
- """
-
- USE_FORM_OVERLOADING = (
- self._METHOD_PARAM or
- (self._CONTENT_PARAM and self._CONTENTTYPE_PARAM)
- )
-
- # We only need to use form overloading on form POST requests.
- if (not USE_FORM_OVERLOADING
- or self._request.method != 'POST'
- or not is_form_media_type(self._content_type)):
- return
-
- # At this point we're committed to parsing the request as form data.
- self._data = self._request.POST
- self._files = self._request.FILES
-
- # Method overloading - change the method and remove the param from the content.
- if (self._METHOD_PARAM and
- self._METHOD_PARAM in self._data):
- self._method = self._data[self._METHOD_PARAM].upper()
- self._data.pop(self._METHOD_PARAM)
-
- # Content overloading - modify the content type, and re-parse.
- if (self._CONTENT_PARAM and
- self._CONTENTTYPE_PARAM and
- self._CONTENT_PARAM in self._data and
- self._CONTENTTYPE_PARAM in self._data):
- self._content_type = self._data[self._CONTENTTYPE_PARAM]
- self._stream = StringIO(self._data[self._CONTENT_PARAM])
- self._data.pop(self._CONTENTTYPE_PARAM)
- self._data.pop(self._CONTENT_PARAM)
- self._data, self._files = self._parse()
-
- def _parse(self):
- """
- Parse the request content, returning a two-tuple of (data, files)
-
- May raise an `UnsupportedMediaType`, or `ParseError` exception.
- """
- if self.stream is None or self.content_type is None:
- return (None, None)
-
- for parser in self.get_parsers():
- if parser.can_handle_request(self.content_type):
- parsed = parser.parse(self.stream, meta=self.META,
- upload_handlers=self.upload_handlers)
- # Parser classes may return the raw data, or a
- # DataAndFiles object. Unpack the result as required.
- try:
- return (parsed.data, parsed.files)
- except AttributeError:
- return (parsed, None)
-
- raise exceptions.UnsupportedMediaType(self._content_type)
-
- def _authenticate(self):
- """
- Attempt to authenticate the request using each authentication instance in turn.
- Returns a two-tuple of (user, authtoken).
- """
- for authentication in self.get_authentications():
- user_auth_tuple = authentication.authenticate(self)
- if not user_auth_tuple is None:
- return user_auth_tuple
- return self._not_authenticated()
-
- def _not_authenticated(self):
- """
- Return a two-tuple of (user, authtoken), representing an
- unauthenticated request.
-
- By default this will be (AnonymousUser, None).
- """
- if api_settings.UNAUTHENTICATED_USER:
- user = api_settings.UNAUTHENTICATED_USER()
- else:
- user = None
-
- if api_settings.UNAUTHENTICATED_TOKEN:
- auth = api_settings.UNAUTHENTICATED_TOKEN()
- else:
- auth = None
-
- return (user, auth)
-
- def __getattr__(self, attr):
- """
- Proxy other attributes to the underlying HttpRequest object.
- """
- return getattr(self._request, attr)
diff --git a/djangorestframework/response.py b/djangorestframework/response.py
deleted file mode 100644
index e1366bdb..00000000
--- a/djangorestframework/response.py
+++ /dev/null
@@ -1,173 +0,0 @@
-"""
-The :mod:`response` module provides :class:`Response` and :class:`ImmediateResponse` classes.
-
-`Response` is a subclass of `HttpResponse`, and can be similarly instantiated and returned
-from any view. It is a bit smarter than Django's `HttpResponse`, for it renders automatically
-its content to a serial format by using a list of :mod:`renderers`.
-
-To determine the content type to which it must render, default behaviour is to use standard
-HTTP Accept header content negotiation. But `Response` also supports overriding the content type
-by specifying an ``_accept=`` parameter in the URL. Also, `Response` will ignore `Accept` headers
-from Internet Explorer user agents and use a sensible browser `Accept` header instead.
-"""
-
-
-import re
-from django.template.response import SimpleTemplateResponse
-from django.core.handlers.wsgi import STATUS_CODE_TEXT
-from djangorestframework.settings import api_settings
-from djangorestframework.utils.mediatypes import order_by_precedence
-from djangorestframework import status
-
-
-MSIE_USER_AGENT_REGEX = re.compile(r'^Mozilla/[0-9]+\.[0-9]+ \([^)]*; MSIE [0-9]+\.[0-9]+[a-z]?;[^)]*\)(?!.* Opera )')
-
-
-class NotAcceptable(Exception):
- pass
-
-
-class Response(SimpleTemplateResponse):
- """
- An HttpResponse that may include content that hasn't yet been serialized.
-
- Kwargs:
- - content(object). The raw content, not yet serialized.
- This must be native Python data that renderers can handle.
- (e.g.: `dict`, `str`, ...)
- - renderer_classes(list/tuple). The renderers to use for rendering the response content.
- """
-
- _ACCEPT_QUERY_PARAM = api_settings.URL_ACCEPT_OVERRIDE
- _IGNORE_IE_ACCEPT_HEADER = True
-
- def __init__(self, content=None, status=None, headers=None, view=None,
- request=None, renderer_classes=None, format=None):
- # First argument taken by `SimpleTemplateResponse.__init__` is template_name,
- # which we don't need
- super(Response, self).__init__(None, status=status)
-
- self.raw_content = content
- self.has_content_body = content is not None
- self.headers = headers and headers[:] or []
- self.view = view
- self.request = request
- self.renderer_classes = renderer_classes
- self.format = format
-
- def get_renderers(self):
- """
- Instantiates and returns the list of renderers the response will use.
- """
- if self.renderer_classes is None:
- renderer_classes = api_settings.DEFAULT_RENDERERS
- else:
- renderer_classes = self.renderer_classes
-
- if self.format:
- return [cls(self.view) for cls in renderer_classes
- if cls.format == self.format]
- return [cls(self.view) for cls in renderer_classes]
-
- @property
- def rendered_content(self):
- """
- The final rendered content. Accessing this attribute triggers the
- complete rendering cycle: selecting suitable renderer, setting
- response's actual content type, rendering data.
- """
- renderer, media_type = self._determine_renderer()
-
- # Set the media type of the response
- self['Content-Type'] = renderer.media_type
-
- # Render the response content
- if self.has_content_body:
- return renderer.render(self.raw_content, media_type)
- return renderer.render()
-
- def render(self):
- try:
- return super(Response, self).render()
- except NotAcceptable:
- response = self._get_406_response()
- return response.render()
-
- @property
- def status_text(self):
- """
- Returns reason text corresponding to our HTTP response status code.
- Provided for convenience.
- """
- return STATUS_CODE_TEXT.get(self.status_code, '')
-
- def _determine_accept_list(self):
- """
- Returns a list of accepted media types. This list is determined from :
-
- 1. overload with `_ACCEPT_QUERY_PARAM`
- 2. `Accept` header of the request
-
- If those are useless, a default value is returned instead.
- """
- request = self.request
-
- if (self._ACCEPT_QUERY_PARAM and
- request.GET.get(self._ACCEPT_QUERY_PARAM, None)):
- # Use _accept parameter override
- return [request.GET.get(self._ACCEPT_QUERY_PARAM)]
- elif (self._IGNORE_IE_ACCEPT_HEADER and
- 'HTTP_USER_AGENT' in request.META and
- MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
- request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'):
- # Ignore MSIE's broken accept behavior except for AJAX requests
- # and do something sensible instead
- return ['text/html', '*/*']
- elif 'HTTP_ACCEPT' in request.META:
- # Use standard HTTP Accept negotiation
- return [token.strip() for token in request.META['HTTP_ACCEPT'].split(',')]
- else:
- # No accept header specified
- return ['*/*']
-
- def _determine_renderer(self):
- """
- Determines the appropriate renderer for the output, given the list of
- accepted media types, and the :attr:`renderer_classes` set on this class.
-
- Returns a 2-tuple of `(renderer, media_type)`
-
- See: RFC 2616, Section 14
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
- """
-
- renderers = self.get_renderers()
- accepts = self._determine_accept_list()
-
- # Not acceptable response - Ignore accept header.
- if self.status_code == 406:
- return (renderers[0], renderers[0].media_type)
-
- # Check the acceptable media types against each renderer,
- # attempting more specific media types first
- # NB. The inner loop here isn't as bad as it first looks :)
- # Worst case is we're looping over len(accept_list) * len(self.renderers)
- for media_type_set in order_by_precedence(accepts):
- for renderer in renderers:
- for media_type in media_type_set:
- if renderer.can_handle_response(media_type):
- return renderer, media_type
-
- # No acceptable renderers were found
- raise NotAcceptable
-
- def _get_406_response(self):
- renderer = self.renderer_classes[0]
- return Response(
- {
- 'detail': 'Could not satisfy the client\'s Accept header',
- 'available_types': [renderer.media_type
- for renderer in self.renderer_classes]
- },
- status=status.HTTP_406_NOT_ACCEPTABLE,
- view=self.view, request=self.request, renderer_classes=[renderer])
diff --git a/djangorestframework/reverse.py b/djangorestframework/reverse.py
deleted file mode 100644
index ba663f98..00000000
--- a/djangorestframework/reverse.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-Provide reverse functions that return fully qualified URLs
-"""
-from django.core.urlresolvers import reverse as django_reverse
-from django.utils.functional import lazy
-
-
-def reverse(viewname, *args, **kwargs):
- """
- Same as `django.core.urlresolvers.reverse`, but optionally takes a request
- and returns a fully qualified URL, using the request to get the base URL.
- """
- request = kwargs.pop('request', None)
- url = django_reverse(viewname, *args, **kwargs)
- if request:
- return request.build_absolute_uri(url)
- return url
-
-
-reverse_lazy = lazy(reverse, str)
diff --git a/djangorestframework/runtests/__init__.py b/djangorestframework/runtests/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/djangorestframework/runtests/__init__.py
+++ /dev/null
diff --git a/djangorestframework/runtests/runcoverage.py b/djangorestframework/runtests/runcoverage.py
deleted file mode 100755
index 4fa7cb8d..00000000
--- a/djangorestframework/runtests/runcoverage.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python
-"""
-Useful tool to run the test suite for djangorestframework and generate a coverage report.
-"""
-
-# http://ericholscher.com/blog/2009/jun/29/enable-setuppy-test-your-django-apps/
-# http://www.travisswicegood.com/2010/01/17/django-virtualenv-pip-and-fabric/
-# http://code.djangoproject.com/svn/django/trunk/tests/runtests.py
-import os
-import sys
-os.environ['DJANGO_SETTINGS_MODULE'] = 'djangorestframework.runtests.settings'
-
-from coverage import coverage
-
-
-def main():
- """Run the tests for djangorestframework and generate a coverage report."""
-
- cov = coverage()
- cov.erase()
- cov.start()
-
- from django.conf import settings
- from django.test.utils import get_runner
- TestRunner = get_runner(settings)
-
- if hasattr(TestRunner, 'func_name'):
- # Pre 1.2 test runners were just functions,
- # and did not support the 'failfast' option.
- import warnings
- warnings.warn(
- 'Function-based test runners are deprecated. Test runners should be classes with a run_tests() method.',
- DeprecationWarning
- )
- failures = TestRunner(['djangorestframework'])
- else:
- test_runner = TestRunner()
- failures = test_runner.run_tests(['djangorestframework'])
- cov.stop()
-
- # Discover the list of all modules that we should test coverage for
- import djangorestframework
-
- project_dir = os.path.dirname(djangorestframework.__file__)
- cov_files = []
-
- for (path, dirs, files) in os.walk(project_dir):
- # Drop tests and runtests directories from the test coverage report
- if os.path.basename(path) == 'tests' or os.path.basename(path) == 'runtests':
- continue
-
- # 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)
- 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)
- if '--html' in sys.argv:
- cov.html_report(cov_files, directory='coverage')
- sys.exit(failures)
-
-if __name__ == '__main__':
- main()
diff --git a/djangorestframework/runtests/runtests.py b/djangorestframework/runtests/runtests.py
deleted file mode 100755
index 1628aff7..00000000
--- a/djangorestframework/runtests/runtests.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python
-
-# http://ericholscher.com/blog/2009/jun/29/enable-setuppy-test-your-django-apps/
-# http://www.travisswicegood.com/2010/01/17/django-virtualenv-pip-and-fabric/
-# http://code.djangoproject.com/svn/django/trunk/tests/runtests.py
-import os
-import sys
-os.environ['DJANGO_SETTINGS_MODULE'] = 'djangorestframework.runtests.settings'
-
-from django.conf import settings
-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)
-
- test_runner = TestRunner()
- if len(sys.argv) == 2:
- test_case = '.' + sys.argv[1]
- elif len(sys.argv) == 1:
- test_case = ''
- else:
- print usage()
- sys.exit(1)
- failures = test_runner.run_tests(['djangorestframework' + test_case])
-
- sys.exit(failures)
-
-if __name__ == '__main__':
- main()
diff --git a/djangorestframework/runtests/settings.py b/djangorestframework/runtests/settings.py
deleted file mode 100644
index da2ae5b8..00000000
--- a/djangorestframework/runtests/settings.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# Django settings for testproject project.
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-DEBUG_PROPAGATE_EXCEPTIONS = True
-
-ADMINS = (
- # ('Your Name', 'your_email@domain.com'),
-)
-
-MANAGERS = ADMINS
-
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
- 'NAME': 'sqlite.db', # Or path to database file if using sqlite3.
- 'USER': '', # Not used with sqlite3.
- 'PASSWORD': '', # Not used with sqlite3.
- 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
- 'PORT': '', # Set to empty string for default. Not used with sqlite3.
- }
-}
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# On Unix systems, a value of None will cause Django to use the same
-# timezone as the operating system.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'Europe/London'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-uk'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# If you set this to False, Django will not format dates, numbers and
-# calendars according to the current locale
-USE_L10N = True
-
-# Absolute filesystem path to the directory that will hold user-uploaded files.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = ''
-
-# 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/"
-MEDIA_URL = ''
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = 'u@x-aj9(hoh#rb-^ymf#g2jx_hp0vj7u5#b@ag1n^seu9e!%cy'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.Loader',
- 'django.template.loaders.app_directories.Loader',
-# 'django.template.loaders.eggs.Loader',
-)
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
-)
-
-ROOT_URLCONF = 'urls'
-
-TEMPLATE_DIRS = (
- # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
- # Always use forward slashes, even on Windows.
- # Don't forget to use absolute paths, not relative paths.
-)
-
-INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.messages',
- # Uncomment the next line to enable the admin:
- # 'django.contrib.admin',
- # Uncomment the next line to enable admin documentation:
- # 'django.contrib.admindocs',
- 'djangorestframework',
- 'djangorestframework.authtoken',
-)
-
-STATIC_URL = '/static/'
-
-import django
-
-if django.VERSION < (1, 3):
- INSTALLED_APPS += ('staticfiles',)
-
-
-# OAuth support is optional, so we only test oauth if it's installed.
-try:
- import oauth_provider
-except ImportError:
- pass
-else:
- INSTALLED_APPS += ('oauth_provider',)
-
-# If we're running on the Jenkins server we want to archive the coverage reports as XML.
-import os
-if os.environ.get('HUDSON_URL', None):
- TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
- TEST_OUTPUT_VERBOSE = True
- TEST_OUTPUT_DESCRIPTIONS = True
- TEST_OUTPUT_DIR = 'xmlrunner'
diff --git a/djangorestframework/runtests/urls.py b/djangorestframework/runtests/urls.py
deleted file mode 100644
index 4b7da787..00000000
--- a/djangorestframework/runtests/urls.py
+++ /dev/null
@@ -1,7 +0,0 @@
-"""
-Blank URLConf just to keep runtests.py happy.
-"""
-from django.conf.urls.defaults import *
-
-urlpatterns = patterns('',
-)
diff --git a/djangorestframework/serializers.py b/djangorestframework/serializers.py
deleted file mode 100644
index 46980ee6..00000000
--- a/djangorestframework/serializers.py
+++ /dev/null
@@ -1,348 +0,0 @@
-from decimal import Decimal
-from django.core.serializers.base import DeserializedObject
-from django.utils.datastructures import SortedDict
-import copy
-import datetime
-import types
-from djangorestframework.fields import *
-
-
-class DictWithMetadata(dict):
- """
- A dict-like object, that can have additional properties attached.
- """
- pass
-
-
-class SortedDictWithMetadata(SortedDict, DictWithMetadata):
- """
- A sorted dict-like object, that can have additional properties attached.
- """
- pass
-
-
-class RecursionOccured(BaseException):
- pass
-
-
-def _is_protected_type(obj):
- """
- True if the object is a native datatype that does not need to
- be serialized further.
- """
- return isinstance(obj, (
- types.NoneType,
- int, long,
- datetime.datetime, datetime.date, datetime.time,
- float, Decimal,
- basestring)
- )
-
-
-def _get_declared_fields(bases, attrs):
- """
- Create a list of serializer field instances from the passed in 'attrs',
- plus any fields on the base classes (in 'bases').
-
- Note that all fields from the base classes are used.
- """
- fields = [(field_name, attrs.pop(field_name))
- for field_name, obj in attrs.items()
- if isinstance(obj, Field)]
- fields.sort(key=lambda x: x[1].creation_counter)
-
- # If this class is subclassing another Serializer, add that Serializer's
- # fields. Note that we loop over the bases in *reverse*. This is necessary
- # in order to the correct order of fields.
- for base in bases[::-1]:
- if hasattr(base, 'base_fields'):
- fields = base.base_fields.items() + fields
-
- return SortedDict(fields)
-
-
-class SerializerMetaclass(type):
- def __new__(cls, name, bases, attrs):
- attrs['base_fields'] = _get_declared_fields(bases, attrs)
- return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
-
-
-class SerializerOptions(object):
- """
- Meta class options for ModelSerializer
- """
- def __init__(self, meta):
- self.nested = getattr(meta, 'nested', False)
- self.fields = getattr(meta, 'fields', ())
- self.exclude = getattr(meta, 'exclude', ())
-
-
-class BaseSerializer(Field):
- class Meta(object):
- pass
-
- _options_class = SerializerOptions
- _dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatability with unsorted implementations.
-
- def __init__(self, data=None, instance=None, context=None, **kwargs):
- super(BaseSerializer, self).__init__(**kwargs)
- self.fields = copy.deepcopy(self.base_fields)
- self.opts = self._options_class(self.Meta)
- self.parent = None
- self.root = None
-
- self.stack = []
- self.context = context or {}
-
- self.init_data = data
- self.instance = instance
-
- self._data = None
- self._errors = None
-
- #####
- # Methods to determine which fields to use when (de)serializing objects.
-
- def default_fields(self, serialize, obj=None, data=None, nested=False):
- """
- Return the complete set of default fields for the object, as a dict.
- """
- return {}
-
- def get_fields(self, serialize, obj=None, data=None, nested=False):
- """
- Returns the complete set of fields for the object as a dict.
-
- This will be the set of any explicitly declared fields,
- plus the set of fields returned by get_default_fields().
- """
- ret = SortedDict()
-
- # Get the explicitly declared fields
- for key, field in self.fields.items():
- ret[key] = field
- # Determine if the declared field corrosponds to a model field.
- try:
- if key == 'pk':
- model_field = obj._meta.pk
- else:
- model_field = obj._meta.get_field_by_name(key)[0]
- except:
- model_field = None
- # Set up the field
- field.initialize(parent=self, model_field=model_field)
-
- # Add in the default fields
- fields = self.default_fields(serialize, obj, data, nested)
- for key, val in fields.items():
- if key not in ret:
- ret[key] = val
-
- # If 'fields' is specified, use those fields, in that order.
- if self.opts.fields:
- new = SortedDict()
- for key in self.opts.fields:
- new[key] = ret[key]
- ret = new
-
- # Remove anything in 'exclude'
- if self.opts.exclude:
- for key in self.opts.exclude:
- ret.pop(key, None)
-
- return ret
-
- #####
- # Field methods - used when the serializer class is itself used as a field.
-
- def initialize(self, parent, model_field=None):
- """
- Same behaviour as usual Field, except that we need to keep track
- of state so that we can deal with handling maximum depth and recursion.
- """
- super(BaseSerializer, self).initialize(parent, model_field)
- self.stack = parent.stack[:]
- if parent.opts.nested and not isinstance(parent.opts.nested, bool):
- self.opts.nested = parent.opts.nested - 1
- else:
- self.opts.nested = parent.opts.nested
-
- #####
- # Methods to convert or revert from objects <--> primative representations.
-
- def get_field_key(self, field_name):
- """
- Return the key that should be used for a given field.
- """
- return field_name
-
- def convert_object(self, obj):
- """
- Core of serialization.
- Convert an object into a dictionary of serialized field values.
- """
- if obj in self.stack and not self.source == '*':
- raise RecursionOccured()
- self.stack.append(obj)
-
- ret = self._dict_class()
- ret.fields = {}
-
- fields = self.get_fields(serialize=True, obj=obj, nested=self.opts.nested)
- for field_name, field in fields.items():
- key = self.get_field_key(field_name)
- try:
- value = field.field_to_native(obj, field_name)
- except RecursionOccured:
- field = self.get_fields(serialize=True, obj=obj, nested=False)[field_name]
- value = field.field_to_native(obj, field_name)
- ret[key] = value
- ret.fields[key] = field
- return ret
-
- def restore_fields(self, data):
- """
- Core of deserialization, together with `restore_object`.
- Converts a dictionary of data into a dictionary of deserialized fields.
- """
- fields = self.get_fields(serialize=False, data=data, nested=self.opts.nested)
- reverted_data = {}
- for field_name, field in fields.items():
- try:
- field.field_from_native(data, field_name, reverted_data)
- except ValidationError as err:
- self._errors[field_name] = list(err.messages)
-
- return reverted_data
-
- def restore_object(self, attrs, instance=None):
- """
- Deserialize a dictionary of attributes into an object instance.
- You should override this method to control how deserialized objects
- are instantiated.
- """
- if instance is not None:
- instance.update(attrs)
- return instance
- return attrs
-
- def to_native(self, obj):
- """
- Serialize objects -> primatives.
- """
- if isinstance(obj, dict):
- return dict([(key, self.to_native(val))
- for (key, val) in obj.items()])
- elif hasattr(obj, '__iter__'):
- return (self.to_native(item) for item in obj)
- return self.convert_object(obj)
-
- def from_native(self, data):
- """
- Deserialize primatives -> objects.
- """
- if hasattr(data, '__iter__') and not isinstance(data, dict):
- # TODO: error data when deserializing lists
- return (self.from_native(item) for item in data)
- self._errors = {}
- attrs = self.restore_fields(data)
- if not self._errors:
- return self.restore_object(attrs, instance=getattr(self, 'instance', None))
-
- @property
- def errors(self):
- """
- Run deserialization and return error data,
- setting self.object if no errors occured.
- """
- if self._errors is None:
- obj = self.from_native(self.init_data)
- if not self._errors:
- self.object = obj
- return self._errors
-
- def is_valid(self):
- return not self.errors
-
- @property
- def data(self):
- if self._data is None:
- self._data = self.to_native(self.instance)
- return self._data
-
-
-class Serializer(BaseSerializer):
- __metaclass__ = SerializerMetaclass
-
-
-class ModelSerializerOptions(SerializerOptions):
- """
- Meta class options for ModelSerializer
- """
- def __init__(self, meta):
- super(ModelSerializerOptions, self).__init__(meta)
- self.model = getattr(meta, 'model', None)
-
-
-class ModelSerializer(RelatedField, Serializer):
- """
- A serializer that deals with model instances and querysets.
- """
- _options_class = ModelSerializerOptions
-
- def default_fields(self, serialize, obj=None, data=None, nested=False):
- """
- Return all the fields that should be serialized for the model.
- """
- if serialize:
- cls = obj.__class__
- else:
- cls = self.opts.model
-
- opts = cls._meta.concrete_model._meta
- pk_field = opts.pk
- while pk_field.rel:
- pk_field = pk_field.rel.to._meta.pk
- fields = [pk_field]
- fields += [field for field in opts.fields if field.serialize]
- fields += [field for field in opts.many_to_many if field.serialize]
-
- ret = SortedDict()
- for model_field in fields:
- if model_field.rel and nested:
- field = self.get_nested_field(model_field)
- elif model_field.rel:
- field = self.get_related_field(model_field)
- else:
- field = self.get_field(model_field)
- field.initialize(parent=self, model_field=model_field)
- ret[model_field.name] = field
- return ret
-
- def get_nested_field(self, model_field):
- """
- Creates a default instance of a nested relational field.
- """
- return ModelSerializer()
-
- def get_related_field(self, model_field):
- """
- Creates a default instance of a flat relational field.
- """
- return PrimaryKeyRelatedField()
-
- def get_field(self, model_field):
- """
- Creates a default instance of a basic field.
- """
- return Field()
-
- def restore_object(self, attrs, instance=None):
- """
- Restore the model instance.
- """
- m2m_data = {}
- for field in self.opts.model._meta.many_to_many:
- if field.name in attrs:
- m2m_data[field.name] = attrs.pop(field.name)
- return DeserializedObject(self.opts.model(**attrs), m2m_data)
diff --git a/djangorestframework/settings.py b/djangorestframework/settings.py
deleted file mode 100644
index e5181f4b..00000000
--- a/djangorestframework/settings.py
+++ /dev/null
@@ -1,125 +0,0 @@
-"""
-Settings for REST framework are all namespaced in the API_SETTINGS setting.
-For example your project's `settings.py` file might look like this:
-
-API_SETTINGS = {
- 'DEFAULT_RENDERERS': (
- 'djangorestframework.renderers.JSONRenderer',
- 'djangorestframework.renderers.YAMLRenderer',
- )
- 'DEFAULT_PARSERS': (
- 'djangorestframework.parsers.JSONParser',
- 'djangorestframework.parsers.YAMLParser',
- )
-}
-
-This module provides the `api_setting` object, that is used to access
-REST framework settings, checking for user settings first, then falling
-back to the defaults.
-"""
-from django.conf import settings
-from django.utils import importlib
-
-
-DEFAULTS = {
- 'DEFAULT_RENDERERS': (
- 'djangorestframework.renderers.JSONRenderer',
- 'djangorestframework.renderers.JSONPRenderer',
- 'djangorestframework.renderers.DocumentingHTMLRenderer',
- 'djangorestframework.renderers.DocumentingPlainTextRenderer',
- ),
- 'DEFAULT_PARSERS': (
- 'djangorestframework.parsers.JSONParser',
- 'djangorestframework.parsers.FormParser'
- ),
- 'DEFAULT_AUTHENTICATION': (
- 'djangorestframework.authentication.SessionAuthentication',
- 'djangorestframework.authentication.UserBasicAuthentication'
- ),
- 'DEFAULT_PERMISSIONS': (),
- 'DEFAULT_THROTTLES': (),
-
- 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
- 'UNAUTHENTICATED_TOKEN': None,
-
- 'FORM_METHOD_OVERRIDE': '_method',
- 'FORM_CONTENT_OVERRIDE': '_content',
- 'FORM_CONTENTTYPE_OVERRIDE': '_content_type',
- 'URL_ACCEPT_OVERRIDE': '_accept',
-
- 'FORMAT_SUFFIX_KWARG': 'format'
-}
-
-
-# List of settings that may be in string import notation.
-IMPORT_STRINGS = (
- 'DEFAULT_RENDERERS',
- 'DEFAULT_PARSERS',
- 'DEFAULT_AUTHENTICATION',
- 'DEFAULT_PERMISSIONS',
- 'DEFAULT_THROTTLES',
- 'UNAUTHENTICATED_USER',
- 'UNAUTHENTICATED_TOKEN'
-)
-
-
-def perform_import(val, setting):
- """
- If the given setting is a string import notation,
- then perform the necessary import or imports.
- """
- if val is None or setting not in IMPORT_STRINGS:
- return val
-
- if isinstance(val, basestring):
- return import_from_string(val, setting)
- elif isinstance(val, (list, tuple)):
- return [import_from_string(item, setting) for item in val]
- return val
-
-
-def import_from_string(val, setting):
- """
- Attempt to import a class from a string representation.
- """
- try:
- # Nod to tastypie's use of importlib.
- parts = val.split('.')
- module_path, class_name = '.'.join(parts[:-1]), parts[-1]
- module = importlib.import_module(module_path)
- return getattr(module, class_name)
- except Exception, e:
- import traceback
- tb = traceback.format_exc()
- import pdb; pdb.set_trace()
- msg = "Could not import '%s' for API setting '%s'" % (val, setting)
- raise ImportError(msg)
-
-
-class APISettings(object):
- """
- A settings object, that allows API settings to be accessed as properties.
- For example:
-
- from djangorestframework.settings import api_settings
- print api_settings.DEFAULT_RENDERERS
-
- Any setting with string import paths will be automatically resolved
- and return the class, rather than the string literal.
- """
- def __getattr__(self, attr):
- if attr not in DEFAULTS.keys():
- raise AttributeError("Invalid API setting: '%s'" % attr)
-
- try:
- # Check if present in user settings
- val = perform_import(settings.API_SETTINGS[attr], attr)
- except (AttributeError, KeyError):
- # Fall back to defaults
- val = perform_import(DEFAULTS[attr], attr)
-
- # Cache the result
- setattr(self, attr, val)
- return val
-
-api_settings = APISettings()
diff --git a/djangorestframework/static/djangorestframework/css/style.css b/djangorestframework/static/djangorestframework/css/style.css
deleted file mode 100644
index d47aa16a..00000000
--- a/djangorestframework/static/djangorestframework/css/style.css
+++ /dev/null
@@ -1,1209 +0,0 @@
-/********************** admin 'base.css' ************************/
-
-body {
- margin: 0;
- padding: 0;
- font-size: 12px;
- font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
- color: #333;
- background: #fff;
-}
-
-/* LINKS */
-
-a:link, a:visited {
- color: #5b80b2;
- text-decoration: none;
-}
-
-a:hover {
- color: #036;
-}
-
-a img {
- border: none;
-}
-
-a.section:link, a.section:visited {
- color: white;
- text-decoration: none;
-}
-
-/* GLOBAL DEFAULTS */
-
-p, ol, ul, dl {
- margin: .2em 0 .8em 0;
-}
-
-p {
- padding: 0;
- line-height: 140%;
-}
-
-h1,h2,h3,h4,h5 {
- font-weight: bold;
-}
-
-h1 {
- font-size: 18px;
- color: #666;
- padding: 0 6px 0 0;
- margin: 0 0 .2em 0;
-}
-
-h2 {
- font-size: 16px;
- margin: 1em 0 .5em 0;
-}
-
-h2.subhead {
- font-weight: normal;
- margin-top: 0;
-}
-
-h3 {
- font-size: 14px;
- margin: .8em 0 .3em 0;
- color: #666;
- font-weight: bold;
-}
-
-h4 {
- font-size: 12px;
- margin: 1em 0 .8em 0;
- padding-bottom: 3px;
-}
-
-h5 {
- font-size: 10px;
- margin: 1.5em 0 .5em 0;
- color: #666;
- text-transform: uppercase;
- letter-spacing: 1px;
-}
-
-ul li {
- list-style-type: square;
- padding: 1px 0;
-}
-
-ul.plainlist {
- margin-left: 0 !important;
-}
-
-ul.plainlist li {
- list-style-type: none;
-}
-
-li ul {
- margin-bottom: 0;
-}
-
-li, dt, dd {
- font-size: 11px;
- line-height: 14px;
-}
-
-dt {
- font-weight: bold;
- margin-top: 4px;
-}
-
-dd {
- margin-left: 0;
-}
-
-form {
- margin: 0;
- padding: 0;
-}
-
-fieldset {
- margin: 0;
- padding: 0;
-}
-
-blockquote {
- font-size: 11px;
- color: #777;
- margin-left: 2px;
- padding-left: 10px;
- border-left: 5px solid #ddd;
-}
-
-code, pre {
- font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
- background: inherit;
- color: #666;
- font-size: 11px;
-}
-
-pre.literal-block {
- margin: 10px;
- background: #eee;
- padding: 6px 8px;
-}
-
-code strong {
- color: #930;
-}
-
-hr {
- clear: both;
- color: #eee;
- background-color: #eee;
- height: 1px;
- border: none;
- margin: 0;
- padding: 0;
- font-size: 1px;
- line-height: 1px;
-}
-
-/* TEXT STYLES & MODIFIERS */
-
-.small {
- font-size: 11px;
-}
-
-.tiny {
- font-size: 10px;
-}
-
-p.tiny {
- margin-top: -2px;
-}
-
-.mini {
- font-size: 9px;
-}
-
-p.mini {
- margin-top: -3px;
-}
-
-.help, p.help {
- font-size: 10px !important;
- color: #999;
-}
-
-p img, h1 img, h2 img, h3 img, h4 img, td img {
- vertical-align: middle;
-}
-
-.quiet, a.quiet:link, a.quiet:visited {
- color: #999 !important;
- font-weight: normal !important;
-}
-
-.quiet strong {
- font-weight: bold !important;
-}
-
-.float-right {
- float: right;
-}
-
-.float-left {
- float: left;
-}
-
-.clear {
- clear: both;
-}
-
-.align-left {
- text-align: left;
-}
-
-.align-right {
- text-align: right;
-}
-
-.example {
- margin: 10px 0;
- padding: 5px 10px;
- background: #efefef;
-}
-
-.nowrap {
- white-space: nowrap;
-}
-
-/* TABLES */
-
-table {
- border-collapse: collapse;
- border-color: #ccc;
-}
-
-td, th {
- font-size: 11px;
- line-height: 13px;
- border-bottom: 1px solid #eee;
- vertical-align: top;
- padding: 5px;
- font-family: "Lucida Grande", Verdana, Arial, sans-serif;
-}
-
-th {
- text-align: left;
- font-size: 12px;
- font-weight: bold;
-}
-
-thead th,
-tfoot td {
- color: #666;
- padding: 2px 5px;
- font-size: 11px;
- background: #e1e1e1 url(../../admin/img/admin/nav-bg.gif) top left repeat-x;
- border-left: 1px solid #ddd;
- border-bottom: 1px solid #ddd;
-}
-
-tfoot td {
- border-bottom: none;
- border-top: 1px solid #ddd;
-}
-
-thead th:first-child,
-tfoot td:first-child {
- border-left: none !important;
-}
-
-thead th.optional {
- font-weight: normal !important;
-}
-
-fieldset table {
- border-right: 1px solid #eee;
-}
-
-tr.row-label td {
- font-size: 9px;
- padding-top: 2px;
- padding-bottom: 0;
- border-bottom: none;
- color: #666;
- margin-top: -1px;
-}
-
-tr.alt {
- background: #f6f6f6;
-}
-
-.row1 {
- background: #EDF3FE;
-}
-
-.row2 {
- background: white;
-}
-
-/* SORTABLE TABLES */
-
-thead th a:link, thead th a:visited {
- color: #666;
- display: block;
-}
-
-table thead th.sorted {
- background-position: bottom left !important;
-}
-
-table thead th.sorted a {
- padding-right: 13px;
-}
-
-table thead th.ascending a {
- background: url(../../admin/img/admin/arrow-up.gif) right .4em no-repeat;
-}
-
-table thead th.descending a {
- background: url(../../admin/img/admin/arrow-down.gif) right .4em no-repeat;
-}
-
-/* ORDERABLE TABLES */
-
-table.orderable tbody tr td:hover {
- cursor: move;
-}
-
-table.orderable tbody tr td:first-child {
- padding-left: 14px;
- background-image: url(../../admin/img/admin/nav-bg-grabber.gif);
- background-repeat: repeat-y;
-}
-
-table.orderable-initalized .order-cell, body>tr>td.order-cell {
- display: none;
-}
-
-/* FORM DEFAULTS */
-
-input, textarea, select, .form-row p {
- margin: 2px 0;
- padding: 2px 3px;
- vertical-align: middle;
- font-family: "Lucida Grande", Verdana, Arial, sans-serif;
- font-weight: normal;
- font-size: 11px;
-}
-
-textarea {
- vertical-align: top !important;
-}
-
-input[type=text], input[type=password], textarea, select, .vTextField {
- border: 1px solid #ccc;
-}
-
-/* FORM BUTTONS */
-
-.button, input[type=submit], input[type=button], .submit-row input {
- background: white url(../../admin/img/admin/nav-bg.gif) bottom repeat-x;
- padding: 3px 5px;
- color: black;
- border: 1px solid #bbb;
- border-color: #ddd #aaa #aaa #ddd;
-}
-
-.button:active, input[type=submit]:active, input[type=button]:active {
- background-image: url(../../admin/img/admin/nav-bg-reverse.gif);
- background-position: top;
-}
-
-.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
- background-image: url(../../admin/img/admin/nav-bg.gif);
- background-position: bottom;
- opacity: 0.4;
-}
-
-.button.default, input[type=submit].default, .submit-row input.default {
- border: 2px solid #5b80b2;
- background: #7CA0C7 url(../../admin/img/admin/default-bg.gif) bottom repeat-x;
- font-weight: bold;
- color: white;
- float: right;
-}
-
-.button.default:active, input[type=submit].default:active {
- background-image: url(../../admin/img/admin/default-bg-reverse.gif);
- background-position: top;
-}
-
-.button[disabled].default, input[type=submit][disabled].default, input[type=button][disabled].default {
- background-image: url(../../admin/img/admin/default-bg.gif);
- background-position: bottom;
- opacity: 0.4;
-}
-
-
-/* MODULES */
-
-.module {
- border: 1px solid #ccc;
- margin-bottom: 5px;
- background: white;
-}
-
-.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
- padding-left: 10px;
- padding-right: 10px;
-}
-
-.module blockquote {
- margin-left: 12px;
-}
-
-.module ul, .module ol {
- margin-left: 1.5em;
-}
-
-.module h3 {
- margin-top: .6em;
-}
-
-.module h2, .module caption, .inline-group h2 {
- margin: 0;
- padding: 2px 5px 3px 5px;
- font-size: 11px;
- text-align: left;
- font-weight: bold;
- background: #7CA0C7 url(../../admin/img/admin/default-bg.gif) top left repeat-x;
- color: white;
-}
-
-.module table {
- border-collapse: collapse;
-}
-
-/* MESSAGES & ERRORS */
-
-ul.messagelist {
- padding: 0 0 5px 0;
- margin: 0;
-}
-
-ul.messagelist li {
- font-size: 12px;
- display: block;
- padding: 4px 5px 4px 25px;
- margin: 0 0 3px 0;
- border-bottom: 1px solid #ddd;
- color: #666;
- background: #ffc url(../../admin/img/admin/icon_success.gif) 5px .3em no-repeat;
-}
-
-ul.messagelist li.warning{
- background-image: url(../../admin/img/admin/icon_alert.gif);
-}
-
-ul.messagelist li.error{
- background-image: url(../../admin/img/admin/icon_error.gif);
-}
-
-.errornote {
- font-size: 12px !important;
- display: block;
- padding: 4px 5px 4px 25px;
- margin: 0 0 3px 0;
- border: 1px solid red;
- color: red;
- background: #ffc url(../../admin/img/admin/icon_error.gif) 5px .3em no-repeat;
-}
-
-ul.errorlist {
- margin: 0 !important;
- padding: 0 !important;
-}
-
-.errorlist li {
- font-size: 12px !important;
- display: block;
- padding: 4px 5px 4px 25px;
- margin: 0 0 3px 0;
- border: 1px solid red;
- color: white;
- background: red url(../../admin/img/admin/icon_alert.gif) 5px .3em no-repeat;
-}
-
-.errorlist li a {
- color: white;
- text-decoration: underline;
-}
-
-td ul.errorlist {
- margin: 0 !important;
- padding: 0 !important;
-}
-
-td ul.errorlist li {
- margin: 0 !important;
-}
-
-.errors {
- background: #ffc;
-}
-
-.errors input, .errors select, .errors textarea {
- border: 1px solid red;
-}
-
-div.system-message {
- background: #ffc;
- margin: 10px;
- padding: 6px 8px;
- font-size: .8em;
-}
-
-div.system-message p.system-message-title {
- padding: 4px 5px 4px 25px;
- margin: 0;
- color: red;
- background: #ffc url(../../admin/img/admin/icon_error.gif) 5px .3em no-repeat;
-}
-
-.description {
- font-size: 12px;
- padding: 5px 0 0 12px;
-}
-
-/* BREADCRUMBS */
-
-div.breadcrumbs {
- background: white url(../../admin/img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
- padding: 2px 8px 3px 8px;
- font-size: 11px;
- color: #999;
- border-top: 1px solid white;
- border-bottom: 1px solid #ccc;
- text-align: left;
-}
-
-/* ACTION ICONS */
-
-.addlink {
- padding-left: 12px;
- background: url(../../admin/img/admin/icon_addlink.gif) 0 .2em no-repeat;
-}
-
-.changelink {
- padding-left: 12px;
- background: url(../../admin/img/admin/icon_changelink.gif) 0 .2em no-repeat;
-}
-
-.deletelink {
- padding-left: 12px;
- background: url(../../admin/img/admin/icon_deletelink.gif) 0 .25em no-repeat;
-}
-
-a.deletelink:link, a.deletelink:visited {
- color: #CC3434;
-}
-
-a.deletelink:hover {
- color: #993333;
-}
-
-/* OBJECT TOOLS */
-
-.object-tools {
- font-size: 10px;
- font-weight: bold;
- font-family: Arial,Helvetica,sans-serif;
- padding-left: 0;
- float: right;
- position: relative;
- margin-top: -2.4em;
- margin-bottom: -2em;
-}
-
-.form-row .object-tools {
- margin-top: 5px;
- margin-bottom: 5px;
- float: none;
- height: 2em;
- padding-left: 3.5em;
-}
-
-.object-tools li {
- display: block;
- float: left;
- background: url(../../admin/img/admin/tool-left.gif) 0 0 no-repeat;
- padding: 0 0 0 8px;
- margin-left: 2px;
- height: 16px;
-}
-
-.object-tools li:hover {
- background: url(../../admin/img/admin/tool-left_over.gif) 0 0 no-repeat;
-}
-
-.object-tools a:link, .object-tools a:visited {
- display: block;
- float: left;
- color: white;
- padding: .1em 14px .1em 8px;
- height: 14px;
- background: #999 url(../../admin/img/admin/tool-right.gif) 100% 0 no-repeat;
-}
-
-.object-tools a:hover, .object-tools li:hover a {
- background: #5b80b2 url(../../admin/img/admin/tool-right_over.gif) 100% 0 no-repeat;
-}
-
-.object-tools a.viewsitelink, .object-tools a.golink {
- background: #999 url(../../admin/img/admin/tooltag-arrowright.gif) top right no-repeat;
- padding-right: 28px;
-}
-
-.object-tools a.viewsitelink:hover, .object-tools a.golink:hover {
- background: #5b80b2 url(../../admin/img/admin/tooltag-arrowright_over.gif) top right no-repeat;
-}
-
-.object-tools a.addlink {
- background: #999 url(../../admin/img/admin/tooltag-add.gif) top right no-repeat;
- padding-right: 28px;
-}
-
-.object-tools a.addlink:hover {
- background: #5b80b2 url(../../admin/img/admin/tooltag-add_over.gif) top right no-repeat;
-}
-
-/* OBJECT HISTORY */
-
-table#change-history {
- width: 100%;
-}
-
-table#change-history tbody th {
- width: 16em;
-}
-
-/* PAGE STRUCTURE */
-
-#container {
- position: relative;
- width: 100%;
- min-width: 760px;
- padding: 0;
-}
-
-#content {
- margin: 10px 15px;
-}
-
-#header {
- width: 100%;
-}
-
-#content-main {
- float: left;
- width: 100%;
-}
-
-#content-related {
- float: right;
- width: 18em;
- position: relative;
- margin-right: -19em;
-}
-
-#footer {
- clear: both;
- padding: 10px;
-}
-
-/* COLUMN TYPES */
-
-.colMS {
- margin-right: 20em !important;
-}
-
-.colSM {
- margin-left: 20em !important;
-}
-
-.colSM #content-related {
- float: left;
- margin-right: 0;
- margin-left: -19em;
-}
-
-.colSM #content-main {
- float: right;
-}
-
-.popup .colM {
- width: 95%;
-}
-
-.subcol {
- float: left;
- width: 46%;
- margin-right: 15px;
-}
-
-.dashboard #content {
- width: 500px;
-}
-
-/* HEADER */
-
-#header {
- background: #417690;
- color: #ffc;
- overflow: hidden;
-}
-
-#header a:link, #header a:visited {
- color: white;
-}
-
-#header a:hover {
- text-decoration: underline;
-}
-
-#branding h1 {
- padding: 0 10px;
- font-size: 18px;
- margin: 8px 0;
- font-weight: normal;
- color: #f4f379;
-}
-
-#branding h2 {
- padding: 0 10px;
- font-size: 14px;
- margin: -8px 0 8px 0;
- font-weight: normal;
- color: #ffc;
-}
-
-#user-tools {
- position: absolute;
- top: 0;
- right: 0;
- padding: 1.2em 10px;
- font-size: 11px;
- text-align: right;
-}
-
-/* SIDEBAR */
-
-#content-related h3 {
- font-size: 12px;
- color: #666;
- margin-bottom: 3px;
-}
-
-#content-related h4 {
- font-size: 11px;
-}
-
-#content-related .module h2 {
- background: #eee url(../../admin/img/admin/nav-bg.gif) bottom left repeat-x;
- color: #666;
-}
-
-/********************** admin 'forms.css' ************************/
-
-/* FORM ROWS */
-
-.form-row {
- overflow: hidden;
- padding: 8px 12px;
- font-size: 11px;
- border-bottom: 1px solid #eee;
-}
-
-.form-row img, .form-row input {
- vertical-align: middle;
-}
-
-form .form-row p {
- padding-left: 0;
- font-size: 11px;
-}
-
-/* FORM LABELS */
-
-form h4 {
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
-}
-
-label {
- font-weight: normal !important;
- color: #666;
- font-size: 12px;
-}
-
-.required label, label.required {
- font-weight: bold !important;
- color: #333 !important;
-}
-
-/* RADIO BUTTONS */
-
-form ul.radiolist li {
- list-style-type: none;
-}
-
-form ul.radiolist label {
- float: none;
- display: inline;
-}
-
-form ul.inline {
- margin-left: 0;
- padding: 0;
-}
-
-form ul.inline li {
- float: left;
- padding-right: 7px;
-}
-
-/* ALIGNED FIELDSETS */
-
-.aligned label {
- display: block;
- padding: 3px 10px 0 0;
- float: left;
- width: 8em;
-}
-
-.aligned ul label {
- display: inline;
- float: none;
- width: auto;
-}
-
-.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
- width: 350px;
-}
-
-form .aligned p, form .aligned ul {
- margin-left: 7em;
- padding-left: 30px;
-}
-
-form .aligned table p {
- margin-left: 0;
- padding-left: 0;
-}
-
-form .aligned p.help {
- padding-left: 38px;
-}
-
-.aligned .vCheckboxLabel {
- float: none !important;
- display: inline;
- padding-left: 4px;
-}
-
-.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
- width: 610px;
-}
-
-.checkbox-row p.help {
- margin-left: 0;
- padding-left: 0 !important;
-}
-
-fieldset .field-box {
- float: left;
- margin-right: 20px;
-}
-
-/* WIDE FIELDSETS */
-
-.wide label {
- width: 15em !important;
-}
-
-form .wide p {
- margin-left: 15em;
-}
-
-form .wide p.help {
- padding-left: 38px;
-}
-
-.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
- width: 450px;
-}
-
-/* COLLAPSED FIELDSETS */
-
-fieldset.collapsed * {
- display: none;
-}
-
-fieldset.collapsed h2, fieldset.collapsed {
- display: block !important;
-}
-
-fieldset.collapsed h2 {
- background-image: url(../../admin/img/admin/nav-bg.gif);
- background-position: bottom left;
- color: #999;
-}
-
-fieldset.collapsed .collapse-toggle {
- background: transparent;
- display: inline !important;
-}
-
-/* MONOSPACE TEXTAREAS */
-
-fieldset.monospace textarea {
- font-family: "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace;
-}
-
-/* SUBMIT ROW */
-
-.submit-row {
- padding: 5px 7px;
- text-align: right;
- background: white url(../../admin/img/admin/nav-bg.gif) 0 100% repeat-x;
- border: 1px solid #ccc;
- margin: 5px 0;
- overflow: hidden;
-}
-
-.submit-row input {
- margin: 0 0 0 5px;
-}
-
-.submit-row p {
- margin: 0.3em;
-}
-
-.submit-row p.deletelink-box {
- float: left;
-}
-
-.submit-row .deletelink {
- background: url(../../admin/img/admin/icon_deletelink.gif) 0 50% no-repeat;
- padding-left: 14px;
-}
-
-/* CUSTOM FORM FIELDS */
-
-.vSelectMultipleField {
- vertical-align: top !important;
-}
-
-.vCheckboxField {
- border: none;
-}
-
-.vDateField, .vTimeField {
- margin-right: 2px;
-}
-
-.vURLField {
- width: 30em;
-}
-
-.vLargeTextField, .vXMLLargeTextField {
- width: 48em;
-}
-
-.flatpages-flatpage #id_content {
- height: 40.2em;
-}
-
-.module table .vPositiveSmallIntegerField {
- width: 2.2em;
-}
-
-.vTextField {
- width: 20em;
-}
-
-.vIntegerField {
- width: 5em;
-}
-
-.vForeignKeyRawIdAdminField {
- width: 5em;
-}
-
-/* INLINES */
-
-.inline-group {
- padding: 0;
- border: 1px solid #ccc;
- margin: 10px 0;
-}
-
-.inline-group .aligned label {
- width: 8em;
-}
-
-.inline-related {
- position: relative;
-}
-
-.inline-related h3 {
- margin: 0;
- color: #666;
- padding: 3px 5px;
- font-size: 11px;
- background: #e1e1e1 url(../../admin/img/admin/nav-bg.gif) top left repeat-x;
- border-bottom: 1px solid #ddd;
-}
-
-.inline-related h3 span.delete {
- float: right;
-}
-
-.inline-related h3 span.delete label {
- margin-left: 2px;
- font-size: 11px;
-}
-
-.inline-related fieldset {
- margin: 0;
- background: #fff;
- border: none;
-}
-
-.inline-related fieldset.module h3 {
- margin: 0;
- padding: 2px 5px 3px 5px;
- font-size: 11px;
- text-align: left;
- font-weight: bold;
- background: #bcd;
- color: #fff;
-}
-
-.inline-group .tabular fieldset.module {
- border: none;
- border-bottom: 1px solid #ddd;
-}
-
-.inline-related.tabular fieldset.module table {
- width: 100%;
-}
-
-.last-related fieldset {
- border: none;
-}
-
-.inline-group .tabular tr.has_original td {
- padding-top: 2em;
-}
-
-.inline-group .tabular tr td.original {
- padding: 2px 0 0 0;
- width: 0;
- _position: relative;
-}
-
-.inline-group .tabular th.original {
- width: 0px;
- padding: 0;
-}
-
-.inline-group .tabular td.original p {
- position: absolute;
- left: 0;
- height: 1.1em;
- padding: 2px 7px;
- overflow: hidden;
- font-size: 9px;
- font-weight: bold;
- color: #666;
- _width: 700px;
-}
-
-.inline-group ul.tools {
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
-.inline-group ul.tools li {
- display: inline;
- padding: 0 5px;
-}
-
-.inline-group div.add-row,
-.inline-group .tabular tr.add-row td {
- color: #666;
- padding: 3px 5px;
- border-bottom: 1px solid #ddd;
- background: #e1e1e1 url(../../admin/img/admin/nav-bg.gif) top left repeat-x;
-}
-
-.inline-group .tabular tr.add-row td {
- padding: 4px 5px 3px;
- border-bottom: none;
-}
-
-.inline-group ul.tools a.add,
-.inline-group div.add-row a,
-.inline-group .tabular tr.add-row td a {
- background: url(../../admin/img/admin/icon_addlink.gif) 0 50% no-repeat;
- padding-left: 14px;
- font-size: 11px;
- outline: 0; /* Remove dotted border around link */
-}
-
-.empty-form {
- display: none;
-}
-
-/* IE7 specific bug fixes */
-
-.submit-row input {
- float: right;
-}
-
-body.login {
- background: #eee;
-}
-
-.login #container {
- background: white;
- border: 1px solid #ccc;
- width: 28em;
- min-width: 300px;
- margin-left: auto;
- margin-right: auto;
- margin-top: 100px;
-}
-
-.login #content-main {
- width: 100%;
-}
-
-.login form {
- margin-top: 1em;
-}
-
-.login .form-row {
- padding: 4px 0;
- float: left;
- width: 100%;
-}
-
-.login .form-row label {
- float: left;
- width: 9em;
- padding-right: 0.5em;
- line-height: 2em;
- text-align: right;
- font-size: 1em;
- color: #333;
-}
-
-.login .form-row #id_username, .login .form-row #id_password {
- width: 14em;
-}
-
-.login span.help {
- font-size: 10px;
- display: block;
-}
-
-.login .submit-row {
- clear: both;
- padding: 1em 0 0 9.4em;
-}
-
-/* Overrides specific to REST framework */
-
-#site-name a {
- color: #F4F379 !important;
-}
-
-.errorlist {
- display: inline !important;
-}
-
-.errorlist li {
- display: inline !important;
- background: white !important;
- color: black !important;
- border: 0 !important;
-}
-
-/* Custom styles */
-
-.version {
- font-size: 8px;
-}
-
-.form-row {
- border-bottom: 0.25em !important;
-}
diff --git a/djangorestframework/status.py b/djangorestframework/status.py
deleted file mode 100644
index f3a5e481..00000000
--- a/djangorestframework/status.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""
-Descriptive HTTP status codes, for code readability.
-
-See RFC 2616 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
-And RFC 6585 - http://tools.ietf.org/html/rfc6585
-"""
-
-HTTP_100_CONTINUE = 100
-HTTP_101_SWITCHING_PROTOCOLS = 101
-HTTP_200_OK = 200
-HTTP_201_CREATED = 201
-HTTP_202_ACCEPTED = 202
-HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
-HTTP_204_NO_CONTENT = 204
-HTTP_205_RESET_CONTENT = 205
-HTTP_206_PARTIAL_CONTENT = 206
-HTTP_300_MULTIPLE_CHOICES = 300
-HTTP_301_MOVED_PERMANENTLY = 301
-HTTP_302_FOUND = 302
-HTTP_303_SEE_OTHER = 303
-HTTP_304_NOT_MODIFIED = 304
-HTTP_305_USE_PROXY = 305
-HTTP_306_RESERVED = 306
-HTTP_307_TEMPORARY_REDIRECT = 307
-HTTP_400_BAD_REQUEST = 400
-HTTP_401_UNAUTHORIZED = 401
-HTTP_402_PAYMENT_REQUIRED = 402
-HTTP_403_FORBIDDEN = 403
-HTTP_404_NOT_FOUND = 404
-HTTP_405_METHOD_NOT_ALLOWED = 405
-HTTP_406_NOT_ACCEPTABLE = 406
-HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
-HTTP_408_REQUEST_TIMEOUT = 408
-HTTP_409_CONFLICT = 409
-HTTP_410_GONE = 410
-HTTP_411_LENGTH_REQUIRED = 411
-HTTP_412_PRECONDITION_FAILED = 412
-HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
-HTTP_414_REQUEST_URI_TOO_LONG = 414
-HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
-HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
-HTTP_417_EXPECTATION_FAILED = 417
-HTTP_428_PRECONDITION_REQUIRED = 428
-HTTP_429_TOO_MANY_REQUESTS = 429
-HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
-HTTP_500_INTERNAL_SERVER_ERROR = 500
-HTTP_501_NOT_IMPLEMENTED = 501
-HTTP_502_BAD_GATEWAY = 502
-HTTP_503_SERVICE_UNAVAILABLE = 503
-HTTP_504_GATEWAY_TIMEOUT = 504
-HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
-HTTP_511_NETWORD_AUTHENTICATION_REQUIRED = 511
diff --git a/djangorestframework/templates/djangorestframework/api.html b/djangorestframework/templates/djangorestframework/api.html
deleted file mode 100644
index fd9bcc98..00000000
--- a/djangorestframework/templates/djangorestframework/api.html
+++ /dev/null
@@ -1,3 +0,0 @@
-{% extends "djangorestframework/base.html" %}
-
-{# Override this template in your own templates directory to customize #} \ No newline at end of file
diff --git a/djangorestframework/templates/djangorestframework/api.txt b/djangorestframework/templates/djangorestframework/api.txt
deleted file mode 100644
index b2f071d6..00000000
--- a/djangorestframework/templates/djangorestframework/api.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-{% autoescape off %}{{ name }}
-
-{{ description }}
-
-HTTP {{ response.status }} {{ response.status_text }}
-{% for key, val in response.headers.items %}{{ key }}: {{ val }}
-{% endfor %}
-{{ content }}{% endautoescape %}
diff --git a/djangorestframework/templates/djangorestframework/base.html b/djangorestframework/templates/djangorestframework/base.html
deleted file mode 100644
index 97e81bbe..00000000
--- a/djangorestframework/templates/djangorestframework/base.html
+++ /dev/null
@@ -1,151 +0,0 @@
-{% load url from future %}
-{% load urlize_quoted_links %}
-{% load add_query_param %}
-{% load optional_login %}
-{% load static %}
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}djangorestframework/css/style.css'/>
- {% block extrastyle %}{% endblock %}
- <title>{% block title %}Django REST framework - {{ name }}{% endblock %}</title>
- {% block extrahead %}{% endblock %}
- {% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
- </head>
- <body class="{% block bodyclass %}{% endblock %}">
- <div id="container">
-
- <div id="header">
- <div id="branding">
- <h1 id="site-name">{% block branding %}<a href='http://django-rest-framework.org'>Django REST framework</a> <span class="version"> v {{ version }}</span>{% endblock %}</h1>
- </div>
- <div id="user-tools">
- {% block userlinks %}
- {% if user.is_active %}
- Welcome, {{ user }}.
- {% optional_login %}
- {% else %}
- {% optional_logout %}
- {% endif %}
- {% endblock %}
- </div>
- {% block nav-global %}{% endblock %}
- </div>
-
- <div class="breadcrumbs">
- {% block breadcrumbs %}
- {% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
- <a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a> {% if not forloop.last %}&rsaquo;{% endif %}
- {% endfor %}
- {% endblock %}
- </div>
-
- <!-- Content -->
- <div id="content" class="{% block coltype %}colM{% endblock %}">
-
- {% if 'OPTIONS' in allowed_methods and api_settings.FORM_METHOD_OVERRIDE %}
- <form action="{{ request.get_full_path }}" method="post">
- {% csrf_token %}
- <input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="OPTIONS" />
- <input type="submit" value="OPTIONS" class="default" />
- </form>
- {% endif %}
-
- <div class='content-main'>
- <h1>{{ name }}</h1>
- <p>{{ description }}</p>
- <div class='module'>
- <pre><b>HTTP {{ response.status_code }} {{ response.status_text }}</b>{% autoescape off %}
-{% for key, val in response.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
-{% endfor %}
-{{ content|urlize_quoted_links }}</pre>{% endautoescape %}</div>
-
- {% if 'GET' in allowed_methods %}
- <form>
- <fieldset class='module aligned'>
- <h2>GET {{ name }}</h2>
- <div class='submit-row' style='margin: 0; border: 0'>
- <a href='{{ request.get_full_path }}' rel="nofollow" style='float: left'>GET</a>
- {% for format in available_formats %}
- {% with FORMAT_PARAM|add:"="|add:format as param %}
- [<a href='{{ request.get_full_path|add_query_param:param }}' rel="nofollow">{{ format }}</a>]
- {% endwith %}
- {% endfor %}
- </div>
- </fieldset>
- </form>
- {% endif %}
-
-
- {# Only display the POST/PUT/DELETE forms if method tunneling via POST forms is enabled and the user has permissions on this view. #}
- {% if response.status_code != 403 %}
-
- {% if 'POST' in allowed_methods %}
- <form action="{{ request.get_full_path }}" method="POST" {% if post_form.is_multipart %}enctype="multipart/form-data"{% endif %}>
- <fieldset class='module aligned'>
- <h2>POST {{ name }}</h2>
- {% csrf_token %}
- {{ post_form.non_field_errors }}
- {% for field in post_form %}
- <div class='form-row'>
- {{ field.label_tag }}
- {{ field }}
- <span class='help'>{{ field.help_text }}</span>
- {{ field.errors }}
- </div>
- {% endfor %}
- <div class='submit-row' style='margin: 0; border: 0'>
- <input type="submit" value="POST" class="default" />
- </div>
- </fieldset>
- </form>
- {% endif %}
-
- {% if 'PUT' in allowed_methods and api_settings.FORM_METHOD_OVERRIDE %}
- <form action="{{ request.get_full_path }}" method="POST" {% if put_form.is_multipart %}enctype="multipart/form-data"{% endif %}>
- <fieldset class='module aligned'>
- <h2>PUT {{ name }}</h2>
- <input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" />
- {% csrf_token %}
- {{ put_form.non_field_errors }}
- {% for field in put_form %}
- <div class='form-row'>
- {{ field.label_tag }}
- {{ field }}
- <span class='help'>{{ field.help_text }}</span>
- {{ field.errors }}
- </div>
- {% endfor %}
- <div class='submit-row' style='margin: 0; border: 0'>
- <input type="submit" value="PUT" class="default" />
- </div>
- </fieldset>
- </form>
- {% endif %}
-
- {% if 'DELETE' in allowed_methods and api_settings.FORM_METHOD_OVERRIDE %}
- <form action="{{ request.get_full_path }}" method="POST">
- <fieldset class='module aligned'>
- <h2>DELETE {{ name }}</h2>
- {% csrf_token %}
- <input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" />
- <div class='submit-row' style='margin: 0; border: 0'>
- <input type="submit" value="DELETE" class="default" />
- </div>
- </fieldset>
- </form>
- {% endif %}
-
- {% endif %}
- </div>
- <!-- END content-main -->
-
- </div>
- <!-- END Content -->
-
- {% block footer %}<div id="footer"></div>{% endblock %}
- </div>
- </body>
-</html>
diff --git a/djangorestframework/templates/djangorestframework/login.html b/djangorestframework/templates/djangorestframework/login.html
deleted file mode 100644
index fd82561f..00000000
--- a/djangorestframework/templates/djangorestframework/login.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{% load url from future %}
-{% load static %}
-<html>
-
- <head>
- <link rel="stylesheet" type="text/css" href='{% get_static_prefix %}djangorestframework/css/style.css'/>
- </head>
-
- <body class="login">
-
- <div id="container">
-
- <div id="header">
- <div id="branding">
- <h1 id="site-name">Django REST framework</h1>
- </div>
- </div>
-
- <div id="content" class="colM">
- <div id="content-main">
- <form method="post" action="{% url 'djangorestframework:login' %}" id="login-form">
- {% csrf_token %}
- <div class="form-row">
- <label for="id_username">Username:</label> {{ form.username }}
- </div>
- <div class="form-row">
- <label for="id_password">Password:</label> {{ form.password }}
- <input type="hidden" name="next" value="{{ next }}" />
- </div>
- <div class="form-row">
- <label>&nbsp;</label><input type="submit" value="Log in">
- </div>
- </form>
- <script type="text/javascript">
- document.getElementById('id_username').focus()
- </script>
- </div>
- <br class="clear">
- </div>
-
- <div id="footer"></div>
-
- </div>
- </body>
-</html>
diff --git a/djangorestframework/templatetags/__init__.py b/djangorestframework/templatetags/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/djangorestframework/templatetags/__init__.py
+++ /dev/null
diff --git a/djangorestframework/templatetags/add_query_param.py b/djangorestframework/templatetags/add_query_param.py
deleted file mode 100644
index 4cf0133b..00000000
--- a/djangorestframework/templatetags/add_query_param.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from django.template import Library
-from urlobject import URLObject
-register = Library()
-
-
-def add_query_param(url, param):
- return unicode(URLObject(url).with_query(param))
-
-
-register.filter('add_query_param', add_query_param)
diff --git a/djangorestframework/templatetags/optional_login.py b/djangorestframework/templatetags/optional_login.py
deleted file mode 100644
index c448c142..00000000
--- a/djangorestframework/templatetags/optional_login.py
+++ /dev/null
@@ -1,32 +0,0 @@
-"""
-Tags to optionally include the login and logout links, depending on if the
-login and logout views are in the urlconf.
-"""
-from django import template
-from django.core.urlresolvers import reverse, NoReverseMatch
-
-register = template.Library()
-
-
-@register.simple_tag(takes_context=True)
-def optional_login(context):
- try:
- login_url = reverse('djangorestframework:login')
- except NoReverseMatch:
- return ''
-
- request = context['request']
- snippet = "<a href='%s?next=%s'>Log in</a>" % (login_url, request.path)
- return snippet
-
-
-@register.simple_tag(takes_context=True)
-def optional_logout(context):
- try:
- logout_url = reverse('djangorestframework:logout')
- except NoReverseMatch:
- return ''
-
- request = context['request']
- snippet = "<a href='%s?next=%s'>Log out</a>" % (logout_url, request.path)
- return snippet
diff --git a/djangorestframework/templatetags/urlize_quoted_links.py b/djangorestframework/templatetags/urlize_quoted_links.py
deleted file mode 100644
index e8852fad..00000000
--- a/djangorestframework/templatetags/urlize_quoted_links.py
+++ /dev/null
@@ -1,102 +0,0 @@
-"""
-Adds the custom filter 'urlize_quoted_links'
-
-This is identical to the built-in filter 'urlize' with the exception that
-single and double quotes are permitted as leading or trailing punctuation.
-
-Almost all of this code is copied verbatim from django.utils.html
-LEADING_PUNCTUATION and TRAILING_PUNCTUATION have been modified
-"""
-
-import re
-import string
-
-from django.utils.safestring import SafeData, mark_safe
-from django.utils.encoding import force_unicode
-from django.utils.html import escape
-from django import template
-
-# Configuration for urlize() function.
-LEADING_PUNCTUATION = ['(', '<', '&lt;', '"', "'"]
-TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '&gt;', '"', "'"]
-
-# List of possible strings used for bullets in bulleted lists.
-DOTS = ['&middot;', '*', '\xe2\x80\xa2', '&#149;', '&bull;', '&#8226;']
-
-unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
-word_split_re = re.compile(r'(\s+)')
-punctuation_re = re.compile('^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % \
- ('|'.join([re.escape(x) for x in LEADING_PUNCTUATION]),
- '|'.join([re.escape(x) for x in TRAILING_PUNCTUATION])))
-simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
-link_target_attribute_re = re.compile(r'(<a [^>]*?)target=[^\s>]+')
-html_gunk_re = re.compile(r'(?:<br clear="all">|<i><\/i>|<b><\/b>|<em><\/em>|<strong><\/strong>|<\/?smallcaps>|<\/?uppercase>)', re.IGNORECASE)
-hard_coded_bullets_re = re.compile(r'((?:<p>(?:%s).*?[a-zA-Z].*?</p>\s*)+)' % '|'.join([re.escape(x) for x in DOTS]), re.DOTALL)
-trailing_empty_content_re = re.compile(r'(?:<p>(?:&nbsp;|\s|<br \/>)*?</p>\s*)+\Z')
-
-
-def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
- """
- Converts any URLs in text into clickable links.
-
- Works on http://, https://, www. links and links ending in .org, .net or
- .com. Links can have trailing punctuation (periods, commas, close-parens)
- and leading punctuation (opening parens) and it'll still do the right
- thing.
-
- If trim_url_limit is not None, the URLs in link text longer than this limit
- will truncated to trim_url_limit-3 characters and appended with an elipsis.
-
- If nofollow is True, the URLs in link text will get a rel="nofollow"
- attribute.
-
- If autoescape is True, the link text and URLs will get autoescaped.
- """
- trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
- safe_input = isinstance(text, SafeData)
- words = word_split_re.split(force_unicode(text))
- nofollow_attr = nofollow and ' rel="nofollow"' or ''
- for i, word in enumerate(words):
- match = None
- if '.' in word or '@' in word or ':' in word:
- match = punctuation_re.match(word)
- if match:
- lead, middle, trail = match.groups()
- # Make URL we want to point to.
- url = None
- if middle.startswith('http://') or middle.startswith('https://'):
- url = middle
- elif middle.startswith('www.') or ('@' not in middle and \
- middle and middle[0] in string.ascii_letters + string.digits and \
- (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
- url = 'http://%s' % middle
- elif '@' in middle and not ':' in middle and simple_email_re.match(middle):
- url = 'mailto:%s' % middle
- nofollow_attr = ''
- # Make link.
- if url:
- trimmed = trim_url(middle)
- if autoescape and not safe_input:
- lead, trail = escape(lead), escape(trail)
- url, trimmed = escape(url), escape(trimmed)
- middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
- words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
- else:
- if safe_input:
- words[i] = mark_safe(word)
- elif autoescape:
- words[i] = escape(word)
- elif safe_input:
- words[i] = mark_safe(word)
- elif autoescape:
- words[i] = escape(word)
- return u''.join(words)
-
-
-#urlize_quoted_links.needs_autoescape = True
-urlize_quoted_links.is_safe = True
-
-# Register urlize_quoted_links as a custom filter
-# http://docs.djangoproject.com/en/dev/howto/custom-template-tags/
-register = template.Library()
-register.filter(urlize_quoted_links)
diff --git a/djangorestframework/tests/__init__.py b/djangorestframework/tests/__init__.py
deleted file mode 100644
index 641b0277..00000000
--- a/djangorestframework/tests/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""Force import of all modules in this package in order to get the standard test runner to pick up the tests. Yowzers."""
-import os
-
-modules = [filename.rsplit('.', 1)[0]
- for filename in os.listdir(os.path.dirname(__file__))
- if filename.endswith('.py') and not filename.startswith('_')]
-__test__ = dict()
-
-for module in modules:
- exec("from djangorestframework.tests.%s import __doc__ as module_doc" % module)
- exec("from djangorestframework.tests.%s import *" % module)
- __test__[module] = module_doc or ""
diff --git a/djangorestframework/tests/accept.py b/djangorestframework/tests/accept.py
deleted file mode 100644
index 7258f461..00000000
--- a/djangorestframework/tests/accept.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from django.conf.urls.defaults import patterns, url, include
-from django.test import TestCase
-
-from djangorestframework.compat import RequestFactory
-from djangorestframework.views import APIView
-from djangorestframework.response import Response
-
-
-# See: http://www.useragentstring.com/
-MSIE_9_USER_AGENT = 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))'
-MSIE_8_USER_AGENT = 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)'
-MSIE_7_USER_AGENT = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)'
-FIREFOX_4_0_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)'
-CHROME_11_0_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.655.0 Safari/534.17'
-SAFARI_5_0_USER_AGENT = 'Mozilla/5.0 (X11; U; Linux x86_64; en-ca) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+'
-OPERA_11_0_MSIE_USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; pl) Opera 11.00'
-OPERA_11_0_OPERA_USER_AGENT = 'Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00'
-
-
-urlpatterns = patterns('',
- url(r'^api', include('djangorestframework.urls', namespace='djangorestframework'))
-)
-
-
-class UserAgentMungingTest(TestCase):
- """
- We need to fake up the accept headers when we deal with MSIE. Blergh.
- http://www.gethifi.com/blog/browser-rest-http-accept-headers
- """
-
- urls = 'djangorestframework.tests.accept'
-
- def setUp(self):
-
- class MockView(APIView):
- permissions = ()
- response_class = Response
-
- def get(self, request):
- return self.response_class({'a': 1, 'b': 2, 'c': 3})
-
- self.req = RequestFactory()
- self.MockView = MockView
- self.view = MockView.as_view()
-
- def test_munge_msie_accept_header(self):
- """Send MSIE user agent strings and ensure that we get an HTML response,
- even if we set a */* accept header."""
- for user_agent in (MSIE_9_USER_AGENT,
- MSIE_8_USER_AGENT,
- MSIE_7_USER_AGENT):
- req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
- resp = self.view(req)
- resp.render()
- self.assertEqual(resp['Content-Type'], 'text/html')
-
- def test_dont_rewrite_msie_accept_header(self):
- """Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
- that we get a JSON response if we set a */* accept header."""
- class IgnoreIEAcceptResponse(Response):
- _IGNORE_IE_ACCEPT_HEADER = False
- view = self.MockView.as_view(response_class=IgnoreIEAcceptResponse)
-
- for user_agent in (MSIE_9_USER_AGENT,
- MSIE_8_USER_AGENT,
- MSIE_7_USER_AGENT):
- req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
- resp = view(req)
- resp.render()
- 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)"""
- for user_agent in (FIREFOX_4_0_USER_AGENT,
- CHROME_11_0_USER_AGENT,
- SAFARI_5_0_USER_AGENT,
- OPERA_11_0_MSIE_USER_AGENT,
- OPERA_11_0_OPERA_USER_AGENT):
- req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
- resp = self.view(req)
- resp.render()
- self.assertEqual(resp['Content-Type'], 'application/json')
diff --git a/djangorestframework/tests/authentication.py b/djangorestframework/tests/authentication.py
deleted file mode 100644
index ddbee4b6..00000000
--- a/djangorestframework/tests/authentication.py
+++ /dev/null
@@ -1,153 +0,0 @@
-from django.conf.urls.defaults import patterns
-from django.contrib.auth.models import User
-from django.test import Client, TestCase
-
-from django.utils import simplejson as json
-from django.http import HttpResponse
-
-from djangorestframework.views import APIView
-from djangorestframework import permissions
-
-from djangorestframework.authtoken.models import Token
-from djangorestframework.authentication import TokenAuthentication
-
-import base64
-
-
-class MockView(APIView):
- permission_classes = (permissions.IsAuthenticated,)
-
- def post(self, request):
- return HttpResponse({'a': 1, 'b': 2, 'c': 3})
-
- def put(self, request):
- return HttpResponse({'a': 1, 'b': 2, 'c': 3})
-
-MockView.authentication_classes += (TokenAuthentication,)
-
-urlpatterns = patterns('',
- (r'^$', MockView.as_view()),
-)
-
-
-class BasicAuthTests(TestCase):
- """Basic authentication"""
- urls = 'djangorestframework.tests.authentication'
-
- def setUp(self):
- self.csrf_client = Client(enforce_csrf_checks=True)
- self.username = 'john'
- self.email = 'lennon@thebeatles.com'
- self.password = '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"""
- auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).strip()
- response = self.csrf_client.post('/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)
- self.assertEqual(response.status_code, 200)
-
- def test_post_json_passing_basic_auth(self):
- """Ensure POSTing form over basic auth with correct credentials passes and does not require CSRF"""
- auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).strip()
- response = self.csrf_client.post('/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth)
- self.assertEqual(response.status_code, 200)
-
- def test_post_form_failing_basic_auth(self):
- """Ensure POSTing form over basic auth without correct credentials fails"""
- response = self.csrf_client.post('/', {'example': 'example'})
- self.assertEqual(response.status_code, 403)
-
- def test_post_json_failing_basic_auth(self):
- """Ensure POSTing json over basic auth without correct credentials fails"""
- response = self.csrf_client.post('/', json.dumps({'example': 'example'}), 'application/json')
- self.assertEqual(response.status_code, 403)
-
-
-class SessionAuthTests(TestCase):
- """User session authentication"""
- urls = 'djangorestframework.tests.authentication'
-
- def setUp(self):
- self.csrf_client = Client(enforce_csrf_checks=True)
- self.non_csrf_client = Client(enforce_csrf_checks=False)
- self.username = 'john'
- self.email = 'lennon@thebeatles.com'
- self.password = 'password'
- self.user = User.objects.create_user(self.username, self.email, self.password)
-
- def tearDown(self):
- self.csrf_client.logout()
-
- def test_post_form_session_auth_failing_csrf(self):
- """
- Ensure POSTing form over session authentication without CSRF token fails.
- """
- self.csrf_client.login(username=self.username, password=self.password)
- response = self.csrf_client.post('/', {'example': 'example'})
- self.assertEqual(response.status_code, 403)
-
- def test_post_form_session_auth_passing(self):
- """
- Ensure POSTing form over session authentication with logged in user and CSRF token passes.
- """
- self.non_csrf_client.login(username=self.username, password=self.password)
- response = self.non_csrf_client.post('/', {'example': 'example'})
- self.assertEqual(response.status_code, 200)
-
- def test_put_form_session_auth_passing(self):
- """
- Ensure PUTting form over session authentication with logged in user and CSRF token passes.
- """
- self.non_csrf_client.login(username=self.username, password=self.password)
- response = self.non_csrf_client.put('/', {'example': 'example'})
- self.assertEqual(response.status_code, 200)
-
- def test_post_form_session_auth_failing(self):
- """
- Ensure POSTing form over session authentication without logged in user fails.
- """
- response = self.csrf_client.post('/', {'example': 'example'})
- self.assertEqual(response.status_code, 403)
-
-
-class TokenAuthTests(TestCase):
- """Token authentication"""
- urls = 'djangorestframework.tests.authentication'
-
- def setUp(self):
- self.csrf_client = Client(enforce_csrf_checks=True)
- self.username = 'john'
- self.email = 'lennon@thebeatles.com'
- self.password = 'password'
- self.user = User.objects.create_user(self.username, self.email, self.password)
-
- self.key = 'abcd1234'
- self.token = Token.objects.create(key=self.key, user=self.user)
-
- def test_post_form_passing_token_auth(self):
- """Ensure POSTing json over token auth with correct credentials passes and does not require CSRF"""
- auth = "Token " + self.key
- response = self.csrf_client.post('/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)
- self.assertEqual(response.status_code, 200)
-
- def test_post_json_passing_token_auth(self):
- """Ensure POSTing form over token auth with correct credentials passes and does not require CSRF"""
- auth = "Token " + self.key
- response = self.csrf_client.post('/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth)
- self.assertEqual(response.status_code, 200)
-
- def test_post_form_failing_token_auth(self):
- """Ensure POSTing form over token auth without correct credentials fails"""
- response = self.csrf_client.post('/', {'example': 'example'})
- self.assertEqual(response.status_code, 403)
-
- def test_post_json_failing_token_auth(self):
- """Ensure POSTing json over token auth without correct credentials fails"""
- response = self.csrf_client.post('/', json.dumps({'example': 'example'}), 'application/json')
- self.assertEqual(response.status_code, 403)
-
- def test_token_has_auto_assigned_key_if_none_provided(self):
- """Ensure creating a token with no key will auto-assign a key"""
- token = Token.objects.create(user=self.user)
- self.assertTrue(bool(token.key))
diff --git a/djangorestframework/tests/breadcrumbs.py b/djangorestframework/tests/breadcrumbs.py
deleted file mode 100644
index ae84106c..00000000
--- a/djangorestframework/tests/breadcrumbs.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from django.conf.urls.defaults import patterns, url
-from django.test import TestCase
-from djangorestframework.utils.breadcrumbs import get_breadcrumbs
-from djangorestframework.views import APIView
-
-
-class Root(APIView):
- pass
-
-
-class ResourceRoot(APIView):
- pass
-
-
-class ResourceInstance(APIView):
- pass
-
-
-class NestedResourceRoot(APIView):
- pass
-
-
-class NestedResourceInstance(APIView):
- pass
-
-urlpatterns = patterns('',
- url(r'^$', Root.as_view()),
- url(r'^resource/$', ResourceRoot.as_view()),
- url(r'^resource/(?P<key>[0-9]+)$', ResourceInstance.as_view()),
- url(r'^resource/(?P<key>[0-9]+)/$', NestedResourceRoot.as_view()),
- url(r'^resource/(?P<key>[0-9]+)/(?P<other>[A-Za-z]+)$', NestedResourceInstance.as_view()),
-)
-
-
-class BreadcrumbTests(TestCase):
- """Tests the breadcrumb functionality used by the HTML renderer."""
-
- urls = 'djangorestframework.tests.breadcrumbs'
-
- def test_root_breadcrumbs(self):
- url = '/'
- self.assertEqual(get_breadcrumbs(url), [('Root', '/')])
-
- def test_resource_root_breadcrumbs(self):
- url = '/resource/'
- self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
- ('Resource Root', '/resource/')])
-
- def test_resource_instance_breadcrumbs(self):
- url = '/resource/123'
- self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
- ('Resource Root', '/resource/'),
- ('Resource Instance', '/resource/123')])
-
- def test_nested_resource_breadcrumbs(self):
- url = '/resource/123/'
- self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
- ('Resource Root', '/resource/'),
- ('Resource Instance', '/resource/123'),
- ('Nested Resource Root', '/resource/123/')])
-
- def test_nested_resource_instance_breadcrumbs(self):
- url = '/resource/123/abc'
- self.assertEqual(get_breadcrumbs(url), [('Root', '/'),
- ('Resource Root', '/resource/'),
- ('Resource Instance', '/resource/123'),
- ('Nested Resource Root', '/resource/123/'),
- ('Nested Resource Instance', '/resource/123/abc')])
-
- def test_broken_url_breadcrumbs_handled_gracefully(self):
- url = '/foobar'
- self.assertEqual(get_breadcrumbs(url), [('Root', '/')])
diff --git a/djangorestframework/tests/description.py b/djangorestframework/tests/description.py
deleted file mode 100644
index b88451eb..00000000
--- a/djangorestframework/tests/description.py
+++ /dev/null
@@ -1,113 +0,0 @@
-from django.test import TestCase
-from djangorestframework.views import APIView
-from djangorestframework.compat import apply_markdown
-
-# We check that docstrings get nicely un-indented.
-DESCRIPTION = """an example docstring
-====================
-
-* list
-* list
-
-another header
---------------
-
- code block
-
-indented
-
-# hash style header #"""
-
-# If markdown is installed we also test it's working
-# (and that our wrapped forces '=' to h2 and '-' to h3)
-
-# We support markdown < 2.1 and markdown >= 2.1
-MARKED_DOWN_lt_21 = """<h2>an example docstring</h2>
-<ul>
-<li>list</li>
-<li>list</li>
-</ul>
-<h3>another header</h3>
-<pre><code>code block
-</code></pre>
-<p>indented</p>
-<h2 id="hash_style_header">hash style header</h2>"""
-
-MARKED_DOWN_gte_21 = """<h2 id="an-example-docstring">an example docstring</h2>
-<ul>
-<li>list</li>
-<li>list</li>
-</ul>
-<h3 id="another-header">another header</h3>
-<pre><code>code block
-</code></pre>
-<p>indented</p>
-<h2 id="hash-style-header">hash style header</h2>"""
-
-
-class TestViewNamesAndDescriptions(TestCase):
- def test_resource_name_uses_classname_by_default(self):
- """Ensure Resource names are based on the classname by default."""
- class MockView(APIView):
- pass
- self.assertEquals(MockView().get_name(), 'Mock')
-
- def test_resource_name_can_be_set_explicitly(self):
- """Ensure Resource names can be set using the 'get_name' method."""
- example = 'Some Other Name'
- class MockView(APIView):
- def get_name(self):
- return example
- self.assertEquals(MockView().get_name(), example)
-
- def test_resource_description_uses_docstring_by_default(self):
- """Ensure Resource names are based on the docstring by default."""
- class MockView(APIView):
- """an example docstring
- ====================
-
- * list
- * list
-
- another header
- --------------
-
- code block
-
- indented
-
- # hash style header #"""
-
- self.assertEquals(MockView().get_description(), DESCRIPTION)
-
- def test_resource_description_can_be_set_explicitly(self):
- """Ensure Resource descriptions can be set using the 'get_description' method."""
- example = 'Some other description'
-
- class MockView(APIView):
- """docstring"""
- def get_description(self):
- return example
- self.assertEquals(MockView().get_description(), 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 'get_description' method."""
- example = 'Some other description'
-
- class MockView(APIView):
- def get_description(self):
- return example
- self.assertEquals(MockView().get_description(), example)
-
- def test_resource_description_can_be_empty(self):
- """Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string."""
- class MockView(APIView):
- pass
- self.assertEquals(MockView().get_description(), '')
-
- def test_markdown(self):
- """Ensure markdown to HTML works as expected"""
- if apply_markdown:
- gte_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_gte_21
- lt_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_lt_21
- self.assertTrue(gte_21_match or lt_21_match)
diff --git a/djangorestframework/tests/files.py b/djangorestframework/tests/files.py
deleted file mode 100644
index 90a613b9..00000000
--- a/djangorestframework/tests/files.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# from django.test import TestCase
-# from django import forms
-
-# from djangorestframework.compat import RequestFactory
-# from djangorestframework.views import View
-# from djangorestframework.response import Response
-
-# import StringIO
-
-
-# class UploadFilesTests(TestCase):
-# """Check uploading of files"""
-# def setUp(self):
-# self.factory = RequestFactory()
-
-# def test_upload_file(self):
-
-# class FileForm(forms.Form):
-# file = forms.FileField()
-
-# class MockView(View):
-# permissions = ()
-# form = FileForm
-
-# def post(self, request, *args, **kwargs):
-# return Response({'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})
-# view = MockView.as_view()
-# response = view(request)
-# self.assertEquals(response.raw_content, {"FILE_CONTENT": "stuff", "FILE_NAME": "stuff.txt"})
diff --git a/djangorestframework/tests/methods.py b/djangorestframework/tests/methods.py
deleted file mode 100644
index e69de29b..00000000
--- a/djangorestframework/tests/methods.py
+++ /dev/null
diff --git a/djangorestframework/tests/mixins.py b/djangorestframework/tests/mixins.py
deleted file mode 100644
index 05ce655d..00000000
--- a/djangorestframework/tests/mixins.py
+++ /dev/null
@@ -1,285 +0,0 @@
-# """Tests for the mixin module"""
-# from django.test import TestCase
-# from djangorestframework import status
-# from djangorestframework.compat import RequestFactory
-# from django.contrib.auth.models import Group, User
-# from djangorestframework.mixins import CreateModelMixin, PaginatorMixin, ReadModelMixin
-# from djangorestframework.resources import ModelResource
-# from djangorestframework.response import Response, ImmediateResponse
-# from djangorestframework.tests.models import CustomUser
-# from djangorestframework.tests.testcases import TestModelsTestCase
-# from djangorestframework.views import View
-
-
-# class TestModelRead(TestModelsTestCase):
-# """Tests on ReadModelMixin"""
-
-# def setUp(self):
-# super(TestModelRead, self).setUp()
-# self.req = RequestFactory()
-
-# def test_read(self):
-# Group.objects.create(name='other group')
-# group = Group.objects.create(name='my group')
-
-# class GroupResource(ModelResource):
-# model = Group
-
-# request = self.req.get('/groups')
-# mixin = ReadModelMixin()
-# mixin.resource = GroupResource
-
-# response = mixin.get(request, id=group.id)
-# self.assertEquals(group.name, response.raw_content.name)
-
-# def test_read_404(self):
-# class GroupResource(ModelResource):
-# model = Group
-
-# request = self.req.get('/groups')
-# mixin = ReadModelMixin()
-# mixin.resource = GroupResource
-
-# self.assertRaises(ImmediateResponse, mixin.get, request, id=12345)
-
-
-# class TestModelCreation(TestModelsTestCase):
-# """Tests on CreateModelMixin"""
-
-# def setUp(self):
-# super(TestModelsTestCase, self).setUp()
-# self.req = RequestFactory()
-
-# def test_creation(self):
-# self.assertEquals(0, Group.objects.count())
-
-# class GroupResource(ModelResource):
-# model = Group
-
-# form_data = {'name': 'foo'}
-# request = self.req.post('/groups', data=form_data)
-# mixin = CreateModelMixin()
-# mixin.resource = GroupResource
-# mixin.CONTENT = form_data
-
-# response = mixin.post(request)
-# self.assertEquals(1, Group.objects.count())
-# self.assertEquals('foo', response.raw_content.name)
-
-# def test_creation_with_m2m_relation(self):
-# class UserResource(ModelResource):
-# model = User
-
-# def url(self, instance):
-# return "/users/%i" % instance.id
-
-# group = Group(name='foo')
-# group.save()
-
-# form_data = {
-# 'username': 'bar',
-# 'password': 'baz',
-# 'groups': [group.id]
-# }
-# request = self.req.post('/groups', data=form_data)
-# cleaned_data = dict(form_data)
-# cleaned_data['groups'] = [group]
-# mixin = CreateModelMixin()
-# mixin.resource = UserResource
-# mixin.CONTENT = cleaned_data
-
-# response = mixin.post(request)
-# self.assertEquals(1, User.objects.count())
-# self.assertEquals(1, response.raw_content.groups.count())
-# self.assertEquals('foo', response.raw_content.groups.all()[0].name)
-
-# def test_creation_with_m2m_relation_through(self):
-# """
-# Tests creation where the m2m relation uses a through table
-# """
-# class UserResource(ModelResource):
-# model = CustomUser
-
-# def url(self, instance):
-# return "/customusers/%i" % instance.id
-
-# form_data = {'username': 'bar0', 'groups': []}
-# request = self.req.post('/groups', data=form_data)
-# cleaned_data = dict(form_data)
-# cleaned_data['groups'] = []
-# mixin = CreateModelMixin()
-# mixin.resource = UserResource
-# mixin.CONTENT = cleaned_data
-
-# response = mixin.post(request)
-# self.assertEquals(1, CustomUser.objects.count())
-# self.assertEquals(0, response.raw_content.groups.count())
-
-# group = Group(name='foo1')
-# group.save()
-
-# form_data = {'username': 'bar1', 'groups': [group.id]}
-# request = self.req.post('/groups', data=form_data)
-# cleaned_data = dict(form_data)
-# cleaned_data['groups'] = [group]
-# mixin = CreateModelMixin()
-# mixin.resource = UserResource
-# mixin.CONTENT = cleaned_data
-
-# response = mixin.post(request)
-# self.assertEquals(2, CustomUser.objects.count())
-# self.assertEquals(1, response.raw_content.groups.count())
-# self.assertEquals('foo1', response.raw_content.groups.all()[0].name)
-
-# group2 = Group(name='foo2')
-# group2.save()
-
-# form_data = {'username': 'bar2', 'groups': [group.id, group2.id]}
-# request = self.req.post('/groups', data=form_data)
-# cleaned_data = dict(form_data)
-# cleaned_data['groups'] = [group, group2]
-# mixin = CreateModelMixin()
-# mixin.resource = UserResource
-# mixin.CONTENT = cleaned_data
-
-# response = mixin.post(request)
-# self.assertEquals(3, CustomUser.objects.count())
-# self.assertEquals(2, response.raw_content.groups.count())
-# self.assertEquals('foo1', response.raw_content.groups.all()[0].name)
-# self.assertEquals('foo2', response.raw_content.groups.all()[1].name)
-
-
-# class MockPaginatorView(PaginatorMixin, View):
-# total = 60
-
-# def get(self, request):
-# return Response(range(0, self.total))
-
-# def post(self, request):
-# return Response({'status': 'OK'}, status=status.HTTP_201_CREATED)
-
-
-# class TestPagination(TestCase):
-# def setUp(self):
-# self.req = RequestFactory()
-
-# def test_default_limit(self):
-# """ Tests if pagination works without overwriting the limit """
-# request = self.req.get('/paginator')
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertEqual(MockPaginatorView.total, content['total'])
-# self.assertEqual(MockPaginatorView.limit, content['per_page'])
-
-# self.assertEqual(range(0, MockPaginatorView.limit), content['results'])
-
-# def test_overwriting_limit(self):
-# """ Tests if the limit can be overwritten """
-# limit = 10
-
-# request = self.req.get('/paginator')
-# response = MockPaginatorView.as_view(limit=limit)(request)
-# content = response.raw_content
-
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertEqual(content['per_page'], limit)
-
-# self.assertEqual(range(0, limit), content['results'])
-
-# def test_limit_param(self):
-# """ Tests if the client can set the limit """
-# from math import ceil
-
-# limit = 5
-# num_pages = int(ceil(MockPaginatorView.total / float(limit)))
-
-# request = self.req.get('/paginator/?limit=%d' % limit)
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertEqual(MockPaginatorView.total, content['total'])
-# self.assertEqual(limit, content['per_page'])
-# self.assertEqual(num_pages, content['pages'])
-
-# def test_exceeding_limit(self):
-# """ Makes sure the client cannot exceed the default limit """
-# from math import ceil
-
-# limit = MockPaginatorView.limit + 10
-# num_pages = int(ceil(MockPaginatorView.total / float(limit)))
-
-# request = self.req.get('/paginator/?limit=%d' % limit)
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertEqual(MockPaginatorView.total, content['total'])
-# self.assertNotEqual(limit, content['per_page'])
-# self.assertNotEqual(num_pages, content['pages'])
-# self.assertEqual(MockPaginatorView.limit, content['per_page'])
-
-# def test_only_works_for_get(self):
-# """ Pagination should only work for GET requests """
-# request = self.req.post('/paginator', data={'content': 'spam'})
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-
-# self.assertEqual(response.status_code, status.HTTP_201_CREATED)
-# self.assertEqual(None, content.get('per_page'))
-# self.assertEqual('OK', content['status'])
-
-# def test_non_int_page(self):
-# """ Tests that it can handle invalid values """
-# request = self.req.get('/paginator/?page=spam')
-# response = MockPaginatorView.as_view()(request)
-
-# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
-# def test_page_range(self):
-# """ Tests that the page range is handle correctly """
-# request = self.req.get('/paginator/?page=0')
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
-# request = self.req.get('/paginator/')
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertEqual(range(0, MockPaginatorView.limit), content['results'])
-
-# num_pages = content['pages']
-
-# request = self.req.get('/paginator/?page=%d' % num_pages)
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertEqual(range(MockPaginatorView.limit*(num_pages-1), MockPaginatorView.total), content['results'])
-
-# request = self.req.get('/paginator/?page=%d' % (num_pages + 1,))
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
-
-# def test_existing_query_parameters_are_preserved(self):
-# """ Tests that existing query parameters are preserved when
-# generating next/previous page links """
-# request = self.req.get('/paginator/?foo=bar&another=something')
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-# self.assertEqual(response.status_code, status.HTTP_200_OK)
-# self.assertTrue('foo=bar' in content['next'])
-# self.assertTrue('another=something' in content['next'])
-# self.assertTrue('page=2' in content['next'])
-
-# def test_duplicate_parameters_are_not_created(self):
-# """ Regression: ensure duplicate "page" parameters are not added to
-# paginated URLs. So page 1 should contain ?page=2, not ?page=1&page=2 """
-# request = self.req.get('/paginator/?page=1')
-# response = MockPaginatorView.as_view()(request)
-# content = response.raw_content
-# self.assertTrue('page=2' in content['next'])
-# self.assertFalse('page=1' in content['next'])
diff --git a/djangorestframework/tests/models.py b/djangorestframework/tests/models.py
deleted file mode 100644
index 4cae68b6..00000000
--- a/djangorestframework/tests/models.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.db import models
-from django.contrib.auth.models import Group
-
-class CustomUser(models.Model):
- """
- A custom user model, which uses a 'through' table for the foreign key
- """
- username = models.CharField(max_length=255, unique=True)
- groups = models.ManyToManyField(
- to=Group, blank=True, null=True, through='UserGroupMap'
- )
-
- @models.permalink
- def get_absolute_url(self):
- return ('custom_user', (), {
- 'pk': self.id
- })
-
-
-class UserGroupMap(models.Model):
- user = models.ForeignKey(to=CustomUser)
- group = models.ForeignKey(to=Group)
-
- @models.permalink
- def get_absolute_url(self):
- return ('user_group_map', (), {
- 'pk': self.id
- })
diff --git a/djangorestframework/tests/modelviews.py b/djangorestframework/tests/modelviews.py
deleted file mode 100644
index 73cb0b2b..00000000
--- a/djangorestframework/tests/modelviews.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# from django.conf.urls.defaults import patterns, url
-# from django.forms import ModelForm
-# from django.contrib.auth.models import Group, User
-# from djangorestframework.resources import ModelResource
-# from djangorestframework.views import ListOrCreateModelView, InstanceModelView
-# from djangorestframework.tests.models import CustomUser
-# from djangorestframework.tests.testcases import TestModelsTestCase
-
-
-# class GroupResource(ModelResource):
-# model = Group
-
-
-# class UserForm(ModelForm):
-# class Meta:
-# model = User
-# exclude = ('last_login', 'date_joined')
-
-
-# class UserResource(ModelResource):
-# model = User
-# form = UserForm
-
-
-# class CustomUserResource(ModelResource):
-# model = CustomUser
-
-# urlpatterns = patterns('',
-# url(r'^users/$', ListOrCreateModelView.as_view(resource=UserResource), name='users'),
-# url(r'^users/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=UserResource)),
-# url(r'^customusers/$', ListOrCreateModelView.as_view(resource=CustomUserResource), name='customusers'),
-# url(r'^customusers/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=CustomUserResource)),
-# url(r'^groups/$', ListOrCreateModelView.as_view(resource=GroupResource), name='groups'),
-# url(r'^groups/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=GroupResource)),
-# )
-
-
-# class ModelViewTests(TestModelsTestCase):
-# """Test the model views djangorestframework provides"""
-# urls = 'djangorestframework.tests.modelviews'
-
-# def test_creation(self):
-# """Ensure that a model object can be created"""
-# self.assertEqual(0, Group.objects.count())
-
-# response = self.client.post('/groups/', {'name': 'foo'})
-
-# self.assertEqual(response.status_code, 201)
-# self.assertEqual(1, Group.objects.count())
-# self.assertEqual('foo', Group.objects.all()[0].name)
-
-# def test_creation_with_m2m_relation(self):
-# """Ensure that a model object with a m2m relation can be created"""
-# group = Group(name='foo')
-# group.save()
-# 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
-# relation uses a through table
-# """
-# group = Group(name='foo')
-# group.save()
-# 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)
diff --git a/djangorestframework/tests/oauthentication.py b/djangorestframework/tests/oauthentication.py
deleted file mode 100644
index 2621317d..00000000
--- a/djangorestframework/tests/oauthentication.py
+++ /dev/null
@@ -1,211 +0,0 @@
-import time
-
-from django.conf.urls.defaults import patterns, url, include
-from django.contrib.auth.models import User
-from django.test import Client, TestCase
-
-from djangorestframework.views import APIView
-
-# Since oauth2 / django-oauth-plus are optional dependancies, we don't want to
-# always run these tests.
-
-# Unfortunatly we can't skip tests easily until 2.7, se we'll just do this for now.
-try:
- import oauth2 as oauth
- from oauth_provider.decorators import oauth_required
- from oauth_provider.models import Resource, Consumer, Token
-
-except ImportError:
- pass
-
-else:
- # Alrighty, we're good to go here.
- class ClientView(APIView):
- 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'^restframework/', include('djangorestframework.urls', namespace='djangorestframework')),
- )
-
- class OAuthTests(TestCase):
- """
- OAuth authentication:
- * the user would like to access his API data from a third-party website
- * the third-party website proposes a link to get that API data
- * the user is redirected to the API and must log in if not authenticated
- * the API displays a webpage to confirm that the user trusts the third-party website
- * if confirmed, the user is redirected to the third-party website through the callback view
- * 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,
- 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
- require the user to be logged in.
- """
- response = self.client.get('/')
- self.assertEqual(response.content, 'Invalid request parameters.')
- 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
- 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.
- """
- return {
- 'oauth_consumer_key': self.CONSUMER_KEY,
- 'oauth_signature_method': 'PLAINTEXT',
- 'oauth_signature': '%s&' % self.CONSUMER_SECRET,
- 'oauth_timestamp': str(int(time.time())),
- 'oauth_nonce': 'requestnonce',
- 'oauth_version': '1.0',
- '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/",
- 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/",
- 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}
- response = self.client.get("/oauth/authorize/", parameters)
- self.assertEqual(response.status_code, 200)
- self.failIf(not response.content.startswith('Fake authorize view for api.example.com with params: oauth_token='))
- self.assertEqual(token.is_approved, 0)
- parameters['authorize_access'] = 1 # fake authorization by the user
- response = self.client.post("/oauth/authorize/", parameters)
- self.assertEqual(response.status_code, 302)
- self.failIf(not response['Location'].startswith('http://api.example.com/request_token_ready?oauth_verifier='))
- 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.
- """
- return {
- 'oauth_consumer_key': self.CONSUMER_KEY,
- 'oauth_token': token.key,
- 'oauth_signature_method': 'PLAINTEXT',
- 'oauth_signature': '%s&%s' % (self.CONSUMER_SECRET, token.secret),
- 'oauth_timestamp': str(int(time.time())),
- 'oauth_nonce': 'accessnonce',
- 'oauth_version': '1.0',
- '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/",
- self._create_request_token_parameters())
- token = list(Token.objects.all())[-1]
- self.client.login(username=self.username, password=self.password)
- parameters = {'oauth_token': token.key,}
- response = self.client.get("/oauth/authorize/", parameters)
- 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)
- self.failIf(not response.content.startswith('oauth_token_secret='))
- access_token = list(Token.objects.filter(token_type=Token.ACCESS))[-1]
- 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.
- """
- parameters = {
- 'oauth_consumer_key': self.CONSUMER_KEY,
- 'oauth_token': access_token.key,
- 'oauth_signature_method': 'HMAC-SHA1',
- 'oauth_timestamp': str(int(time.time())),
- 'oauth_nonce': 'accessresourcenonce',
- 'oauth_version': '1.0',
- }
- oauth_request = oauth.Request.from_token_and_callback(access_token,
- http_url='http://testserver/', parameters=parameters)
- signature_method = oauth.SignatureMethod_HMAC_SHA1()
- 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/",
- self._create_request_token_parameters())
- token = list(Token.objects.all())[-1]
- self.client.login(username=self.username, password=self.password)
- parameters = {'oauth_token': token.key,}
- response = self.client.get("/oauth/authorize/", parameters)
- parameters['authorize_access'] = 1 # fake authorization by the user
- response = self.client.post("/oauth/authorize/", parameters)
- 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)
- self.assertEqual(response.content, '{"resource": "Protected!"}')
diff --git a/djangorestframework/tests/package.py b/djangorestframework/tests/package.py
deleted file mode 100644
index d18128f7..00000000
--- a/djangorestframework/tests/package.py
+++ /dev/null
@@ -1,11 +0,0 @@
-"""Tests for the djangorestframework package setup."""
-from django.test import TestCase
-import djangorestframework
-
-class TestVersion(TestCase):
- """Simple sanity test to check the VERSION exists"""
-
- def test_version(self):
- """Ensure the VERSION exists."""
- djangorestframework.VERSION
-
diff --git a/djangorestframework/tests/parsers.py b/djangorestframework/tests/parsers.py
deleted file mode 100644
index c9b6afd0..00000000
--- a/djangorestframework/tests/parsers.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# """
-# ..
-# >>> from djangorestframework.parsers import FormParser
-# >>> from djangorestframework.compat import RequestFactory
-# >>> from djangorestframework.views import View
-# >>> from StringIO import StringIO
-# >>> from urllib import urlencode
-# >>> 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
-# from tempfile import TemporaryFile
-# from django.test import TestCase
-# from djangorestframework.compat import RequestFactory
-# 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.
-# files is a sequence of (name, filename, value) elements for data to be uploaded as files
-# Return (content_type, body)."""
-# BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
-# CRLF = '\r\n'
-# L = []
-# for (key, value) in fields:
-# L.append('--' + BOUNDARY)
-# L.append('Content-Disposition: form-data; name="%s"' % key)
-# L.append('')
-# L.append(value)
-# for (key, filename, value) in files:
-# L.append('--' + BOUNDARY)
-# L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
-# L.append('Content-Type: %s' % get_content_type(filename))
-# L.append('')
-# L.append(value)
-# L.append('--' + BOUNDARY + '--')
-# L.append('')
-# 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()
-# self.content_type, self.body = encode_multipart_formdata([('key1', 'val1'), ('key1', 'val2')],
-# [('file1', 'pic.jpg', 'blablabla'), ('file1', 't.txt', 'blobloblo')])
-#
-# def test_multipartparser(self):
-# """Ensure that MultiPartParser can parse multipart/form-data that contains a mix of several files and parameters."""
-# post_req = RequestFactory().post('/', self.body, content_type=self.content_type)
-# view = View()
-# view.request = post_req
-# (data, files) = MultiPartParser(view).parse(StringIO(self.body))
-# self.assertEqual(data['key1'], 'val1')
-# self.assertEqual(files['file1'].read(), 'blablabla')
-
-from StringIO import StringIO
-from django import forms
-from django.test import TestCase
-from djangorestframework.parsers import FormParser
-from djangorestframework.parsers import XMLParser
-import datetime
-
-
-class Form(forms.Form):
- field1 = forms.CharField(max_length=3)
- field2 = forms.CharField()
-
-
-class TestFormParser(TestCase):
- def setUp(self):
- self.string = "field1=abc&field2=defghijk"
-
- def test_parse(self):
- """ Make sure the `QueryDict` works OK """
- parser = FormParser()
-
- stream = StringIO(self.string)
- data = parser.parse(stream)
-
- 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"
- }
- ]
- }
-
- def test_parse(self):
- parser = XMLParser()
- data = parser.parse(self._input)
- self.assertEqual(data, self._data)
-
- def test_complex_data_parse(self):
- parser = XMLParser()
- data = parser.parse(self._complex_data_input)
- self.assertEqual(data, self._complex_data)
diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py
deleted file mode 100644
index 718c903f..00000000
--- a/djangorestframework/tests/renderers.py
+++ /dev/null
@@ -1,384 +0,0 @@
-import re
-
-from django.conf.urls.defaults import patterns, url, include
-from django.test import TestCase
-
-from djangorestframework import status
-from djangorestframework.response import Response
-from djangorestframework.views import APIView
-from djangorestframework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
- XMLRenderer, JSONPRenderer, DocumentingHTMLRenderer
-from djangorestframework.parsers import YAMLParser, XMLParser
-
-from StringIO import StringIO
-import datetime
-from decimal import Decimal
-
-
-DUMMYSTATUS = status.HTTP_200_OK
-DUMMYCONTENT = 'dummycontent'
-
-RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x
-RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x
-
-
-expected_results = [
- ((elem for elem in [1, 2, 3]), JSONRenderer, '[1, 2, 3]') # Generator
-]
-
-
-class BasicRendererTests(TestCase):
- def test_expected_results(self):
- for value, renderer_cls, expected in expected_results:
- output = renderer_cls().render(value)
- self.assertEquals(output, expected)
-
-
-class RendererA(BaseRenderer):
- media_type = 'mock/renderera'
- format = "formata"
-
- def render(self, obj=None, media_type=None):
- return RENDERER_A_SERIALIZER(obj)
-
-
-class RendererB(BaseRenderer):
- media_type = 'mock/rendererb'
- format = "formatb"
-
- def render(self, obj=None, media_type=None):
- return RENDERER_B_SERIALIZER(obj)
-
-
-class MockView(APIView):
- renderer_classes = (RendererA, RendererB)
-
- def get(self, request, **kwargs):
- response = Response(DUMMYCONTENT, status=DUMMYSTATUS)
- return response
-
-
-class MockGETView(APIView):
-
- def get(self, request, **kwargs):
- return Response({'foo': ['bar', 'baz']})
-
-
-class HTMLView(APIView):
- renderer_classes = (DocumentingHTMLRenderer, )
-
- def get(self, request, **kwargs):
- return Response('text')
-
-
-class HTMLView1(APIView):
- renderer_classes = (DocumentingHTMLRenderer, JSONRenderer)
-
- def get(self, request, **kwargs):
- return Response('text')
-
-urlpatterns = patterns('',
- url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
- url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
- url(r'^jsonp/jsonrenderer$', MockGETView.as_view(renderer_classes=[JSONRenderer, JSONPRenderer])),
- url(r'^jsonp/nojsonrenderer$', MockGETView.as_view(renderer_classes=[JSONPRenderer])),
- url(r'^html$', HTMLView.as_view()),
- url(r'^html1$', HTMLView1.as_view()),
- url(r'^api', include('djangorestframework.urls', namespace='djangorestframework'))
-)
-
-
-class RendererEndToEndTests(TestCase):
- """
- End-to-end testing of renderers using an RendererMixin on a generic view.
- """
-
- urls = 'djangorestframework.tests.renderers'
-
- def test_default_renderer_serializes_content(self):
- """If the Accept header is not set the default renderer should serialize the response."""
- resp = self.client.get('/')
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_head_method_serializes_no_content(self):
- """No response must be included in HEAD requests."""
- resp = self.client.head('/')
- self.assertEquals(resp.status_code, DUMMYSTATUS)
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, '')
-
- def test_default_renderer_serializes_content_on_accept_any(self):
- """If the Accept header is set to */* the default renderer should serialize the response."""
- resp = self.client.get('/', HTTP_ACCEPT='*/*')
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_specified_renderer_serializes_content_default_case(self):
- """If the Accept header is set the specified renderer should serialize the response.
- (In this case we check that works for the default renderer)"""
- resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type)
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_specified_renderer_serializes_content_non_default_case(self):
- """If the Accept header is set the specified renderer should serialize the response.
- (In this case we check that works for a non-default renderer)"""
- resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type)
- 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)
- self.assertEquals(resp['Content-Type'], RendererB.media_type)
- self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
- """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
- resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
- self.assertEquals(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE)
-
- def test_specified_renderer_serializes_content_on_format_query(self):
- """If a 'format' query is specified, the renderer with the matching
- format attribute should serialize the response."""
- resp = self.client.get('/?format=%s' % RendererB.format)
- 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_format_kwargs(self):
- """If a 'format' keyword arg is specified, the renderer with the matching
- format attribute should serialize the response."""
- resp = self.client.get('/something.formatb')
- 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_is_used_on_format_query_with_matching_accept(self):
- """If both a 'format' query and a matching Accept header specified,
- the renderer with the matching format attribute should serialize the response."""
- resp = self.client.get('/?format=%s' % RendererB.format,
- HTTP_ACCEPT=RendererB.media_type)
- self.assertEquals(resp['Content-Type'], RendererB.media_type)
- self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_conflicting_format_query_and_accept_ignores_accept(self):
- """If a 'format' query is specified that does not match the Accept
- header, we should only honor the 'format' query string."""
- resp = self.client.get('/?format=%s' % RendererB.format,
- HTTP_ACCEPT='dummy')
- self.assertEquals(resp['Content-Type'], RendererB.media_type)
- self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
-
-_flat_repr = '{"foo": ["bar", "baz"]}'
-_indented_repr = '{\n "foo": [\n "bar",\n "baz"\n ]\n}'
-
-
-def strip_trailing_whitespace(content):
- """
- Seems to be some inconsistencies re. trailing whitespace with
- different versions of the json lib.
- """
- return re.sub(' +\n', '\n', content)
-
-
-class JSONRendererTests(TestCase):
- """
- Tests specific to the JSON Renderer
- """
-
- def test_without_content_type_args(self):
- """
- Test basic JSON rendering.
- """
- obj = {'foo': ['bar', 'baz']}
- renderer = JSONRenderer(None)
- content = renderer.render(obj, 'application/json')
- # Fix failing test case which depends on version of JSON library.
- self.assertEquals(content, _flat_repr)
-
- def test_with_content_type_args(self):
- """
- 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(strip_trailing_whitespace(content), _indented_repr)
-
-
-class JSONPRendererTests(TestCase):
- """
- Tests specific to the JSONP Renderer
- """
-
- urls = 'djangorestframework.tests.renderers'
-
- def test_without_callback_with_json_renderer(self):
- """
- Test JSONP rendering with View JSON Renderer.
- """
- resp = self.client.get('/jsonp/jsonrenderer',
- HTTP_ACCEPT='application/javascript')
- self.assertEquals(resp.status_code, 200)
- self.assertEquals(resp['Content-Type'], 'application/javascript')
- self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
-
- def test_without_callback_without_json_renderer(self):
- """
- Test JSONP rendering without View JSON Renderer.
- """
- resp = self.client.get('/jsonp/nojsonrenderer',
- HTTP_ACCEPT='application/javascript')
- self.assertEquals(resp.status_code, 200)
- self.assertEquals(resp['Content-Type'], 'application/javascript')
- self.assertEquals(resp.content, 'callback(%s);' % _flat_repr)
-
- def test_with_callback(self):
- """
- Test JSONP rendering with callback function name.
- """
- callback_func = 'myjsonpcallback'
- resp = self.client.get('/jsonp/nojsonrenderer?callback=' + callback_func,
- HTTP_ACCEPT='application/javascript')
- self.assertEquals(resp.status_code, 200)
- self.assertEquals(resp['Content-Type'], 'application/javascript')
- self.assertEquals(resp.content, '%s(%s);' % (callback_func, _flat_repr))
-
-
-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.
- """
- obj = {'foo': ['bar', 'baz']}
- 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()
-
- content = renderer.render(obj, 'application/yaml')
- data = parser.parse(StringIO(content))
- self.assertEquals(obj, data)
-
-
-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(None)
- content = renderer.render({'field': 'astring'}, 'application/xml')
- self.assertXMLContains(content, '<field>astring</field>')
-
- def test_render_integer(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer(None)
- content = renderer.render({'field': 111}, 'application/xml')
- self.assertXMLContains(content, '<field>111</field>')
-
- def test_render_datetime(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer(None)
- 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(None)
- 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(None)
- 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(None)
- content = renderer.render({'field': None}, 'application/xml')
- self.assertXMLContains(content, '<field></field>')
-
- def test_render_complex_data(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer(None)
- content = renderer.render(self._complex_data, 'application/xml')
- self.assertXMLContains(content, '<sub_name>first</sub_name>')
- self.assertXMLContains(content, '<sub_name>second</sub_name>')
-
- def test_render_and_parse_complex_data(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer(None)
- 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))
diff --git a/djangorestframework/tests/request.py b/djangorestframework/tests/request.py
deleted file mode 100644
index 51e3660c..00000000
--- a/djangorestframework/tests/request.py
+++ /dev/null
@@ -1,266 +0,0 @@
-"""
-Tests for content parsing, and form-overloaded content parsing.
-"""
-from django.conf.urls.defaults import patterns
-from django.contrib.auth.models import User
-from django.test import TestCase, Client
-
-from djangorestframework import status
-from djangorestframework.authentication import SessionAuthentication
-from djangorestframework.utils import RequestFactory
-from djangorestframework.parsers import (
- FormParser,
- MultiPartParser,
- PlainTextParser,
-)
-from djangorestframework.request import Request
-from djangorestframework.response import Response
-from djangorestframework.views import APIView
-
-
-factory = RequestFactory()
-
-
-class TestMethodOverloading(TestCase):
- def test_GET_method(self):
- """
- GET requests identified.
- """
- request = factory.get('/')
- self.assertEqual(request.method, 'GET')
-
- def test_POST_method(self):
- """
- POST requests identified.
- """
- request = factory.post('/')
- self.assertEqual(request.method, 'POST')
-
- def test_HEAD_method(self):
- """
- HEAD requests identified.
- """
- request = factory.head('/')
- self.assertEqual(request.method, 'HEAD')
-
- def test_overloaded_method(self):
- """
- POST requests can be overloaded to another method by setting a
- reserved form field
- """
- request = factory.post('/', {Request._METHOD_PARAM: 'DELETE'})
- self.assertEqual(request.method, 'DELETE')
-
-
-class TestContentParsing(TestCase):
- def test_standard_behaviour_determines_no_content_GET(self):
- """
- Ensure request.DATA returns None for GET request with no content.
- """
- request = factory.get('/')
- self.assertEqual(request.DATA, None)
-
- def test_standard_behaviour_determines_no_content_HEAD(self):
- """
- Ensure request.DATA returns None for HEAD request.
- """
- request = factory.head('/')
- self.assertEqual(request.DATA, None)
-
- def test_standard_behaviour_determines_form_content_POST(self):
- """
- Ensure request.DATA returns content for POST request with form content.
- """
- data = {'qwerty': 'uiop'}
- parsers = (FormParser, MultiPartParser)
- request = factory.post('/', data, parser=parsers)
- self.assertEqual(request.DATA.items(), data.items())
-
- def test_standard_behaviour_determines_non_form_content_POST(self):
- """
- Ensure request.DATA returns content for POST request with
- non-form content.
- """
- content = 'qwerty'
- content_type = 'text/plain'
- parsers = (PlainTextParser,)
- request = factory.post('/', content, content_type=content_type,
- parsers=parsers)
- self.assertEqual(request.DATA, content)
-
- def test_standard_behaviour_determines_form_content_PUT(self):
- """
- Ensure request.DATA returns content for PUT request with form content.
- """
- data = {'qwerty': 'uiop'}
- parsers = (FormParser, MultiPartParser)
-
- from django import VERSION
-
- if VERSION >= (1, 5):
- from django.test.client import MULTIPART_CONTENT, BOUNDARY, encode_multipart
- request = factory.put('/', encode_multipart(BOUNDARY, data), parsers=parsers,
- content_type=MULTIPART_CONTENT)
- else:
- request = factory.put('/', data, parsers=parsers)
-
- self.assertEqual(request.DATA.items(), data.items())
-
- def test_standard_behaviour_determines_non_form_content_PUT(self):
- """
- Ensure request.DATA returns content for PUT request with
- non-form content.
- """
- content = 'qwerty'
- content_type = 'text/plain'
- parsers = (PlainTextParser, )
- request = factory.put('/', content, content_type=content_type,
- parsers=parsers)
- self.assertEqual(request.DATA, content)
-
- def test_overloaded_behaviour_allows_content_tunnelling(self):
- """
- Ensure request.DATA returns content for overloaded POST request.
- """
- content = 'qwerty'
- content_type = 'text/plain'
- data = {
- Request._CONTENT_PARAM: content,
- Request._CONTENTTYPE_PARAM: content_type
- }
- parsers = (PlainTextParser, )
- request = factory.post('/', data, parsers=parsers)
- self.assertEqual(request.DATA, content)
-
- # def test_accessing_post_after_data_form(self):
- # """
- # Ensures request.POST can be accessed after request.DATA in
- # form request.
- # """
- # data = {'qwerty': 'uiop'}
- # request = factory.post('/', data=data)
- # self.assertEqual(request.DATA.items(), data.items())
- # self.assertEqual(request.POST.items(), data.items())
-
- # def test_accessing_post_after_data_for_json(self):
- # """
- # Ensures request.POST can be accessed after request.DATA in
- # json request.
- # """
- # data = {'qwerty': 'uiop'}
- # content = json.dumps(data)
- # content_type = 'application/json'
- # parsers = (JSONParser, )
-
- # request = factory.post('/', content, content_type=content_type,
- # parsers=parsers)
- # self.assertEqual(request.DATA.items(), data.items())
- # self.assertEqual(request.POST.items(), [])
-
- # def test_accessing_post_after_data_for_overloaded_json(self):
- # """
- # Ensures request.POST can be accessed after request.DATA in overloaded
- # json request.
- # """
- # data = {'qwerty': 'uiop'}
- # content = json.dumps(data)
- # content_type = 'application/json'
- # parsers = (JSONParser, )
- # form_data = {Request._CONTENT_PARAM: content,
- # Request._CONTENTTYPE_PARAM: content_type}
-
- # request = factory.post('/', form_data, parsers=parsers)
- # self.assertEqual(request.DATA.items(), data.items())
- # self.assertEqual(request.POST.items(), form_data.items())
-
- # def test_accessing_data_after_post_form(self):
- # """
- # Ensures request.DATA can be accessed after request.POST in
- # form request.
- # """
- # data = {'qwerty': 'uiop'}
- # parsers = (FormParser, MultiPartParser)
- # request = factory.post('/', data, parsers=parsers)
-
- # self.assertEqual(request.POST.items(), data.items())
- # self.assertEqual(request.DATA.items(), data.items())
-
- # def test_accessing_data_after_post_for_json(self):
- # """
- # Ensures request.DATA can be accessed after request.POST in
- # json request.
- # """
- # data = {'qwerty': 'uiop'}
- # content = json.dumps(data)
- # content_type = 'application/json'
- # parsers = (JSONParser, )
- # request = factory.post('/', content, content_type=content_type,
- # parsers=parsers)
- # self.assertEqual(request.POST.items(), [])
- # self.assertEqual(request.DATA.items(), data.items())
-
- # def test_accessing_data_after_post_for_overloaded_json(self):
- # """
- # Ensures request.DATA can be accessed after request.POST in overloaded
- # json request
- # """
- # data = {'qwerty': 'uiop'}
- # content = json.dumps(data)
- # content_type = 'application/json'
- # parsers = (JSONParser, )
- # form_data = {Request._CONTENT_PARAM: content,
- # Request._CONTENTTYPE_PARAM: content_type}
-
- # request = factory.post('/', form_data, parsers=parsers)
- # self.assertEqual(request.POST.items(), form_data.items())
- # self.assertEqual(request.DATA.items(), data.items())
-
-
-class MockView(APIView):
- authentication_classes = (SessionAuthentication,)
-
- def post(self, request):
- if request.POST.get('example') is not None:
- return Response(status=status.HTTP_200_OK)
-
- return Response(status=status.INTERNAL_SERVER_ERROR)
-
-urlpatterns = patterns('',
- (r'^$', MockView.as_view()),
-)
-
-
-class TestContentParsingWithAuthentication(TestCase):
- urls = 'djangorestframework.tests.request'
-
- def setUp(self):
- self.csrf_client = Client(enforce_csrf_checks=True)
- self.username = 'john'
- self.email = 'lennon@thebeatles.com'
- self.password = 'password'
- self.user = User.objects.create_user(self.username, self.email, self.password)
-
- def test_user_logged_in_authentication_has_POST_when_not_logged_in(self):
- """
- Ensures request.POST exists after SessionAuthentication when user
- doesn't log in.
- """
- content = {'example': 'example'}
-
- response = self.client.post('/', content)
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- response = self.csrf_client.post('/', content)
- self.assertEqual(status.HTTP_200_OK, response.status_code)
-
- # def test_user_logged_in_authentication_has_post_when_logged_in(self):
- # """Ensures request.POST exists after UserLoggedInAuthentication when user does log in"""
- # self.client.login(username='john', password='password')
- # self.csrf_client.login(username='john', password='password')
- # content = {'example': 'example'}
-
- # response = self.client.post('/', content)
- # self.assertEqual(status.OK, response.status_code, "POST data is malformed")
-
- # response = self.csrf_client.post('/', content)
- # self.assertEqual(status.OK, response.status_code, "POST data is malformed")
diff --git a/djangorestframework/tests/response.py b/djangorestframework/tests/response.py
deleted file mode 100644
index 0483d826..00000000
--- a/djangorestframework/tests/response.py
+++ /dev/null
@@ -1,309 +0,0 @@
-import json
-import unittest
-
-from django.conf.urls.defaults import patterns, url, include
-from django.test import TestCase
-
-from djangorestframework.response import Response, NotAcceptable
-from djangorestframework.views import APIView
-from djangorestframework.compat import RequestFactory
-from djangorestframework import status
-from djangorestframework.renderers import (
- BaseRenderer,
- JSONRenderer,
- DocumentingHTMLRenderer,
- DEFAULT_RENDERERS
-)
-
-
-class MockPickleRenderer(BaseRenderer):
- media_type = 'application/pickle'
-
-
-class MockJsonRenderer(BaseRenderer):
- media_type = 'application/json'
-
-
-class TestResponseDetermineRenderer(TestCase):
-
- def get_response(self, url='', accept_list=[], renderer_classes=[]):
- kwargs = {}
- if accept_list is not None:
- kwargs['HTTP_ACCEPT'] = ','.join(accept_list)
- request = RequestFactory().get(url, **kwargs)
- return Response(request=request, renderer_classes=renderer_classes)
-
- def test_determine_accept_list_accept_header(self):
- """
- Test that determine_accept_list takes the Accept header.
- """
- accept_list = ['application/pickle', 'application/json']
- response = self.get_response(accept_list=accept_list)
- self.assertEqual(response._determine_accept_list(), accept_list)
-
- def test_determine_accept_list_default(self):
- """
- Test that determine_accept_list takes the default renderer if Accept is not specified.
- """
- response = self.get_response(accept_list=None)
- self.assertEqual(response._determine_accept_list(), ['*/*'])
-
- def test_determine_accept_list_overriden_header(self):
- """
- Test Accept header overriding.
- """
- accept_list = ['application/pickle', 'application/json']
- response = self.get_response(url='?_accept=application/x-www-form-urlencoded',
- accept_list=accept_list)
- self.assertEqual(response._determine_accept_list(), ['application/x-www-form-urlencoded'])
-
- def test_determine_renderer(self):
- """
- Test that right renderer is chosen, in the order of Accept list.
- """
- accept_list = ['application/pickle', 'application/json']
- renderer_classes = (MockPickleRenderer, MockJsonRenderer)
- response = self.get_response(accept_list=accept_list, renderer_classes=renderer_classes)
- renderer, media_type = response._determine_renderer()
- self.assertEqual(media_type, 'application/pickle')
- self.assertTrue(isinstance(renderer, MockPickleRenderer))
-
- renderer_classes = (MockJsonRenderer, )
- response = self.get_response(accept_list=accept_list, renderer_classes=renderer_classes)
- renderer, media_type = response._determine_renderer()
- self.assertEqual(media_type, 'application/json')
- self.assertTrue(isinstance(renderer, MockJsonRenderer))
-
- def test_determine_renderer_default(self):
- """
- Test determine renderer when Accept was not specified.
- """
- renderer_classes = (MockPickleRenderer, )
- response = self.get_response(accept_list=None, renderer_classes=renderer_classes)
- renderer, media_type = response._determine_renderer()
- self.assertEqual(media_type, '*/*')
- self.assertTrue(isinstance(renderer, MockPickleRenderer))
-
- def test_determine_renderer_no_renderer(self):
- """
- Test determine renderer when no renderer can satisfy the Accept list.
- """
- accept_list = ['application/json']
- renderer_classes = (MockPickleRenderer, )
- response = self.get_response(accept_list=accept_list, renderer_classes=renderer_classes)
- self.assertRaises(NotAcceptable, response._determine_renderer)
-
-
-class TestResponseRenderContent(TestCase):
- def get_response(self, url='', accept_list=[], content=None, renderer_classes=None):
- request = RequestFactory().get(url, HTTP_ACCEPT=','.join(accept_list))
- return Response(request=request, content=content, renderer_classes=renderer_classes or DEFAULT_RENDERERS)
-
- def test_render(self):
- """
- Test rendering simple data to json.
- """
- content = {'a': 1, 'b': [1, 2, 3]}
- content_type = 'application/json'
- response = self.get_response(accept_list=[content_type], content=content)
- response = response.render()
- self.assertEqual(json.loads(response.content), content)
- self.assertEqual(response['Content-Type'], content_type)
-
- def test_render_no_renderer(self):
- """
- Test rendering response when no renderer can satisfy accept.
- """
- content = 'bla'
- content_type = 'weirdcontenttype'
- response = self.get_response(accept_list=[content_type], content=content)
- response = response.render()
- self.assertEqual(response.status_code, 406)
- self.assertIsNotNone(response.content)
-
- # def test_render_renderer_raises_ImmediateResponse(self):
- # """
- # Test rendering response when renderer raises ImmediateResponse
- # """
- # class PickyJSONRenderer(BaseRenderer):
- # """
- # A renderer that doesn't make much sense, just to try
- # out raising an ImmediateResponse
- # """
- # media_type = 'application/json'
-
- # def render(self, obj=None, media_type=None):
- # raise ImmediateResponse({'error': '!!!'}, status=400)
-
- # response = self.get_response(
- # accept_list=['application/json'],
- # renderers=[PickyJSONRenderer, JSONRenderer]
- # )
- # response = response.render()
- # self.assertEqual(response.status_code, 400)
- # self.assertEqual(response.content, json.dumps({'error': '!!!'}))
-
-
-DUMMYSTATUS = status.HTTP_200_OK
-DUMMYCONTENT = 'dummycontent'
-
-RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x
-RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x
-
-
-class RendererA(BaseRenderer):
- media_type = 'mock/renderera'
- format = "formata"
-
- def render(self, obj=None, media_type=None):
- return RENDERER_A_SERIALIZER(obj)
-
-
-class RendererB(BaseRenderer):
- media_type = 'mock/rendererb'
- format = "formatb"
-
- def render(self, obj=None, media_type=None):
- return RENDERER_B_SERIALIZER(obj)
-
-
-class MockView(APIView):
- renderer_classes = (RendererA, RendererB)
-
- def get(self, request, **kwargs):
- return Response(DUMMYCONTENT, status=DUMMYSTATUS)
-
-
-class HTMLView(APIView):
- renderer_classes = (DocumentingHTMLRenderer, )
-
- def get(self, request, **kwargs):
- return Response('text')
-
-
-class HTMLView1(APIView):
- renderer_classes = (DocumentingHTMLRenderer, JSONRenderer)
-
- def get(self, request, **kwargs):
- return Response('text')
-
-
-urlpatterns = patterns('',
- url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
- url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
- url(r'^html$', HTMLView.as_view()),
- url(r'^html1$', HTMLView1.as_view()),
- url(r'^restframework', include('djangorestframework.urls', namespace='djangorestframework'))
-)
-
-
-# TODO: Clean tests bellow - remove duplicates with above, better unit testing, ...
-class RendererIntegrationTests(TestCase):
- """
- End-to-end testing of renderers using an ResponseMixin on a generic view.
- """
-
- urls = 'djangorestframework.tests.response'
-
- def test_default_renderer_serializes_content(self):
- """If the Accept header is not set the default renderer should serialize the response."""
- resp = self.client.get('/')
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_head_method_serializes_no_content(self):
- """No response must be included in HEAD requests."""
- resp = self.client.head('/')
- self.assertEquals(resp.status_code, DUMMYSTATUS)
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, '')
-
- def test_default_renderer_serializes_content_on_accept_any(self):
- """If the Accept header is set to */* the default renderer should serialize the response."""
- resp = self.client.get('/', HTTP_ACCEPT='*/*')
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_specified_renderer_serializes_content_default_case(self):
- """If the Accept header is set the specified renderer should serialize the response.
- (In this case we check that works for the default renderer)"""
- resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type)
- self.assertEquals(resp['Content-Type'], RendererA.media_type)
- self.assertEquals(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_specified_renderer_serializes_content_non_default_case(self):
- """If the Accept header is set the specified renderer should serialize the response.
- (In this case we check that works for a non-default renderer)"""
- resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type)
- 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)
- self.assertEquals(resp['Content-Type'], RendererB.media_type)
- self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- @unittest.skip('can\'t pass because view is a simple Django view and response is an ImmediateResponse')
- def test_unsatisfiable_accept_header_on_request_returns_406_status(self):
- """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response."""
- resp = self.client.get('/', HTTP_ACCEPT='foo/bar')
- self.assertEquals(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE)
-
- def test_specified_renderer_serializes_content_on_format_query(self):
- """If a 'format' query is specified, the renderer with the matching
- format attribute should serialize the response."""
- resp = self.client.get('/?format=%s' % RendererB.format)
- 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_format_kwargs(self):
- """If a 'format' keyword arg is specified, the renderer with the matching
- format attribute should serialize the response."""
- resp = self.client.get('/something.formatb')
- 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_is_used_on_format_query_with_matching_accept(self):
- """If both a 'format' query and a matching Accept header specified,
- the renderer with the matching format attribute should serialize the response."""
- resp = self.client.get('/?format=%s' % RendererB.format,
- HTTP_ACCEPT=RendererB.media_type)
- self.assertEquals(resp['Content-Type'], RendererB.media_type)
- self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
- def test_conflicting_format_query_and_accept_ignores_accept(self):
- """If a 'format' query is specified that does not match the Accept
- header, we should only honor the 'format' query string."""
- resp = self.client.get('/?format=%s' % RendererB.format,
- HTTP_ACCEPT='dummy')
- self.assertEquals(resp['Content-Type'], RendererB.media_type)
- self.assertEquals(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT))
- self.assertEquals(resp.status_code, DUMMYSTATUS)
-
-
-class Issue122Tests(TestCase):
- """
- Tests that covers #122.
- """
- urls = 'djangorestframework.tests.response'
-
- def test_only_html_renderer(self):
- """
- Test if no infinite recursion occurs.
- """
- self.client.get('/html')
-
- def test_html_renderer_is_first(self):
- """
- Test if no infinite recursion occurs.
- """
- self.client.get('/html1')
diff --git a/djangorestframework/tests/reverse.py b/djangorestframework/tests/reverse.py
deleted file mode 100644
index b4791135..00000000
--- a/djangorestframework/tests/reverse.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from django.conf.urls.defaults import patterns, url
-from django.test import TestCase
-from django.utils import simplejson as json
-
-from djangorestframework.renderers import JSONRenderer
-from djangorestframework.reverse import reverse
-from djangorestframework.views import APIView
-from djangorestframework.response import Response
-
-
-class MyView(APIView):
- """
- Mock resource which simply returns a URL, so that we can ensure
- that reversed URLs are fully qualified.
- """
- renderers = (JSONRenderer, )
-
- def get(self, request):
- return Response(reverse('myview', request=request))
-
-
-urlpatterns = patterns('',
- url(r'^myview$', MyView.as_view(), name='myview'),
-)
-
-
-class ReverseTests(TestCase):
- """
- Tests for fully qualifed URLs when using `reverse`.
- """
- urls = 'djangorestframework.tests.reverse'
-
- def test_reversed_urls_are_fully_qualified(self):
- response = self.client.get('/myview')
- self.assertEqual(json.loads(response.content), 'http://testserver/myview')
diff --git a/djangorestframework/tests/serializer.py b/djangorestframework/tests/serializer.py
deleted file mode 100644
index 93ae81ee..00000000
--- a/djangorestframework/tests/serializer.py
+++ /dev/null
@@ -1,117 +0,0 @@
-import datetime
-from django.test import TestCase
-from djangorestframework import serializers
-
-
-class Comment(object):
- def __init__(self, email, content, created):
- self.email = email
- self.content = content
- self.created = created or datetime.datetime.now()
-
- def __eq__(self, other):
- return all([getattr(self, attr) == getattr(other, attr)
- for attr in ('email', 'content', 'created')])
-
-
-class CommentSerializer(serializers.Serializer):
- email = serializers.EmailField()
- content = serializers.CharField(max_length=1000)
- created = serializers.DateTimeField()
-
- def restore_object(self, data, instance=None):
- if instance is None:
- return Comment(**data)
- for key, val in data.items():
- setattr(instance, key, val)
- return instance
-
-
-class BasicTests(TestCase):
- def setUp(self):
- self.comment = Comment(
- 'tom@example.com',
- 'Happy new year!',
- datetime.datetime(2012, 1, 1)
- )
- self.data = {
- 'email': 'tom@example.com',
- 'content': 'Happy new year!',
- 'created': datetime.datetime(2012, 1, 1)
- }
-
- def test_empty(self):
- serializer = CommentSerializer()
- expected = {
- 'email': '',
- 'content': '',
- 'created': None
- }
- self.assertEquals(serializer.data, expected)
-
- def test_serialization(self):
- serializer = CommentSerializer(instance=self.comment)
- expected = self.data
- self.assertEquals(serializer.data, expected)
-
- def test_deserialization_for_create(self):
- serializer = CommentSerializer(self.data)
- expected = self.comment
- self.assertEquals(serializer.is_valid(), True)
- self.assertEquals(serializer.object, expected)
- self.assertFalse(serializer.object is expected)
-
- def test_deserialization_for_update(self):
- serializer = CommentSerializer(self.data, instance=self.comment)
- expected = self.comment
- self.assertEquals(serializer.is_valid(), True)
- self.assertEquals(serializer.object, expected)
- self.assertTrue(serializer.object is expected)
-
-
-class ValidationTests(TestCase):
- def setUp(self):
- self.comment = Comment(
- 'tom@example.com',
- 'Happy new year!',
- datetime.datetime(2012, 1, 1)
- )
- self.data = {
- 'email': 'tom@example.com',
- 'content': 'x' * 1001,
- 'created': datetime.datetime(2012, 1, 1)
- }
-
- def test_deserialization_for_create(self):
- serializer = CommentSerializer(self.data)
- self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']})
-
- def test_deserialization_for_update(self):
- serializer = CommentSerializer(self.data, instance=self.comment)
- self.assertEquals(serializer.is_valid(), False)
- self.assertEquals(serializer.errors, {'content': [u'Ensure this value has at most 1000 characters (it has 1001).']})
-
-
-class MetadataTests(TestCase):
- # def setUp(self):
- # self.comment = Comment(
- # 'tomchristie',
- # 'Happy new year!',
- # datetime.datetime(2012, 1, 1)
- # )
- # self.data = {
- # 'email': 'tomchristie',
- # 'content': 'Happy new year!',
- # 'created': datetime.datetime(2012, 1, 1)
- # }
-
- def test_empty(self):
- serializer = CommentSerializer()
- expected = {
- 'email': serializers.CharField,
- 'content': serializers.CharField,
- 'created': serializers.DateTimeField
- }
- for field_name, field in expected.items():
- self.assertTrue(isinstance(serializer.data.fields[field_name], field))
diff --git a/djangorestframework/tests/status.py b/djangorestframework/tests/status.py
deleted file mode 100644
index 53ecae11..00000000
--- a/djangorestframework/tests/status.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""Tests for the status module"""
-from django.test import TestCase
-from djangorestframework import status
-
-
-class TestStatus(TestCase):
- """Simple sanity test to check the status module"""
-
- def test_status(self):
- """Ensure the status module is present and correct."""
- self.assertEquals(200, status.HTTP_200_OK)
- self.assertEquals(404, status.HTTP_404_NOT_FOUND)
diff --git a/djangorestframework/tests/testcases.py b/djangorestframework/tests/testcases.py
deleted file mode 100644
index 51b4afbc..00000000
--- a/djangorestframework/tests/testcases.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# http://djangosnippets.org/snippets/1011/
-from django.conf import settings
-from django.core.management import call_command
-from django.db.models import loading
-from django.test import TestCase
-
-NO_SETTING = ('!', None)
-
-class TestSettingsManager(object):
- """
- A class which can modify some Django settings temporarily for a
- test and then revert them to their original values later.
-
- Automatically handles resyncing the DB if INSTALLED_APPS is
- modified.
-
- """
- def __init__(self):
- self._original_settings = {}
-
- def set(self, **kwargs):
- for k,v in kwargs.iteritems():
- self._original_settings.setdefault(k, getattr(settings, k,
- NO_SETTING))
- setattr(settings, k, v)
- if 'INSTALLED_APPS' in kwargs:
- self.syncdb()
-
- def syncdb(self):
- loading.cache.loaded = False
- call_command('syncdb', verbosity=0)
-
- def revert(self):
- for k,v in self._original_settings.iteritems():
- if v == NO_SETTING:
- delattr(settings, k)
- else:
- setattr(settings, k, v)
- if 'INSTALLED_APPS' in self._original_settings:
- self.syncdb()
- self._original_settings = {}
-
-
-class SettingsTestCase(TestCase):
- """
- A subclass of the Django TestCase with a settings_manager
- attribute which is an instance of TestSettingsManager.
-
- Comes with a tearDown() method that calls
- self.settings_manager.revert().
-
- """
- def __init__(self, *args, **kwargs):
- super(SettingsTestCase, self).__init__(*args, **kwargs)
- self.settings_manager = TestSettingsManager()
-
- def tearDown(self):
- self.settings_manager.revert()
-
-class TestModelsTestCase(SettingsTestCase):
- def setUp(self, *args, **kwargs):
- installed_apps = tuple(settings.INSTALLED_APPS) + ('djangorestframework.tests',)
- self.settings_manager.set(INSTALLED_APPS=installed_apps)
diff --git a/djangorestframework/tests/throttling.py b/djangorestframework/tests/throttling.py
deleted file mode 100644
index 3033614f..00000000
--- a/djangorestframework/tests/throttling.py
+++ /dev/null
@@ -1,144 +0,0 @@
-"""
-Tests for the throttling implementations in the permissions module.
-"""
-
-from django.test import TestCase
-from django.contrib.auth.models import User
-from django.core.cache import cache
-
-from djangorestframework.compat import RequestFactory
-from djangorestframework.views import APIView
-from djangorestframework.throttling import UserRateThrottle
-from djangorestframework.response import Response
-
-
-class User3SecRateThrottle(UserRateThrottle):
- rate = '3/sec'
- scope = 'seconds'
-
-
-class User3MinRateThrottle(UserRateThrottle):
- rate = '3/min'
- scope = 'minutes'
-
-
-class MockView(APIView):
- throttle_classes = (User3SecRateThrottle,)
-
- def get(self, request):
- return Response('foo')
-
-
-class MockView_MinuteThrottling(APIView):
- throttle_classes = (User3MinRateThrottle,)
-
- def get(self, request):
- return Response('foo')
-
-
-class ThrottlingTests(TestCase):
- 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
- """
- request = self.factory.get('/')
- for dummy in range(4):
- response = MockView.as_view()(request)
- self.assertEqual(429, response.status_code)
-
- def set_throttle_timer(self, view, value):
- """
- Explicitly set the timer, overriding time.time()
- """
- view.throttle_classes[0].timer = lambda self: value
-
- def test_request_throttling_expires(self):
- """
- Ensure request rate is limited for a limited duration only
- """
- self.set_throttle_timer(MockView, 0)
-
- request = self.factory.get('/')
- for dummy in range(4):
- response = MockView.as_view()(request)
- self.assertEqual(429, response.status_code)
-
- # Advance the timer by one second
- self.set_throttle_timer(MockView, 1)
-
- 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')
- for dummy in range(3):
- view.as_view()(request)
- 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
- PerUserThrottles
- """
- self.ensure_is_throttled(MockView, 200)
-
- 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
- set properly.
- """
- request = self.factory.get('/')
- for timer, expect in expected_headers:
- self.set_throttle_timer(view, timer)
- response = view.as_view()(request)
- if expect is not None:
- self.assertEquals(response['X-Throttle-Wait-Seconds'], expect)
- else:
- self.assertFalse('X-Throttle-Wait-Seconds' in response.headers)
-
- def test_seconds_fields(self):
- """
- Ensure for second based throttles.
- """
- self.ensure_response_header_contains_proper_throttle_field(MockView,
- ((0, None),
- (0, None),
- (0, None),
- (0, '1')
- ))
-
- def test_minutes_fields(self):
- """
- Ensure for minute based throttles.
- """
- self.ensure_response_header_contains_proper_throttle_field(MockView_MinuteThrottling,
- ((0, None),
- (0, None),
- (0, None),
- (0, '60')
- ))
-
- def test_next_rate_remains_constant_if_followed(self):
- """
- If a client follows the recommended next request rate,
- the throttling rate should stay constant.
- """
- self.ensure_response_header_contains_proper_throttle_field(MockView_MinuteThrottling,
- ((0, None),
- (20, None),
- (40, None),
- (60, None),
- (80, None)
- ))
diff --git a/djangorestframework/tests/validators.py b/djangorestframework/tests/validators.py
deleted file mode 100644
index 80ad2b17..00000000
--- a/djangorestframework/tests/validators.py
+++ /dev/null
@@ -1,329 +0,0 @@
-# from django import forms
-# from django.db import models
-# from django.test import TestCase
-# from djangorestframework.response import ImmediateResponse
-# from djangorestframework.views import View
-
-
-# class TestDisabledValidations(TestCase):
-# """Tests on FormValidator with validation disabled by setting form to None"""
-
-# def test_disabled_form_validator_returns_content_unchanged(self):
-# """If the view's form attribute is None then FormValidator(view).validate_request(content, None)
-# should just return the content unmodified."""
-# class DisabledFormResource(FormResource):
-# form = None
-
-# class MockView(View):
-# resource = DisabledFormResource
-
-# view = MockView()
-# content = {'qwerty': 'uiop'}
-# self.assertEqual(FormResource(view).validate_request(content, None), content)
-
-# def test_disabled_form_validator_get_bound_form_returns_none(self):
-# """If the view's form attribute is None on then
-# FormValidator(view).get_bound_form(content) should just return None."""
-# class DisabledFormResource(FormResource):
-# form = None
-
-# class MockView(View):
-# resource = DisabledFormResource
-
-# view = MockView()
-# 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."""
-
-# class DisabledModelFormView(View):
-# resource = ModelResource
-
-# view = DisabledModelFormView()
-# 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)
-
-
-# class TestNonFieldErrors(TestCase):
-# """Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
-
-# def test_validate_failed_due_to_non_field_error_returns_appropriate_message(self):
-# """If validation fails with a non-field error, ensure the response a non-field error"""
-# class MockForm(forms.Form):
-# 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
-
-# class MockResource(FormResource):
-# form = MockForm
-
-# class MockView(View):
-# pass
-
-# view = MockView()
-# content = {'field1': 'example1', 'field2': 'example2'}
-# try:
-# MockResource(view).validate_request(content, None)
-# except ImmediateResponse, exc:
-# response = exc.response
-# self.assertEqual(response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
-# else:
-# self.fail('ImmediateResponse was not raised')
-
-
-# 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):
-# 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.MockFormView = MockFormView
-# self.MockModelFormView = MockModelFormView
-
-# def validation_returns_content_unchanged_if_already_valid_and_clean(self, validator):
-# """If the content is already valid and clean then validate(content) should just return the content unmodified."""
-# content = {'qwerty': 'uiop'}
-# self.assertEqual(validator.validate_request(content, None), content)
-
-# def validation_failure_raises_response_exception(self, validator):
-# """If form validation fails a ResourceException 400 (Bad Request) should be raised."""
-# content = {}
-# self.assertRaises(ImmediateResponse, validator.validate_request, content, None)
-
-# def validation_does_not_allow_extra_fields_by_default(self, validator):
-# """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'}
-# self.assertRaises(ImmediateResponse, 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'}
-# validator._validate(content, None, allowed_extra_fields=('extra',))
-
-# def validation_allows_unknown_fields_if_explicitly_allowed(self, validator):
-# """If we set ``unknown_form_fields`` on the form resource, then don't
-# raise errors on unexpected request data"""
-# content = {'qwerty': 'uiop', 'extra': 'extra'}
-# validator.allow_unknown_form_fields = True
-# self.assertEqual({'qwerty': u'uiop'},
-# validator.validate_request(content, None),
-# "Resource didn't accept unknown fields.")
-# validator.allow_unknown_form_fields = False
-
-# 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'}
-# 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 = {}
-# try:
-# validator.validate_request(content, None)
-# except ImmediateResponse, exc:
-# response = exc.response
-# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
-# else:
-# self.fail('ResourceException was not raised')
-
-# def validation_failed_due_to_field_error_returns_appropriate_message(self, validator):
-# """If validation fails due to a field error, ensure the response contains a single field error"""
-# content = {'qwerty': ''}
-# try:
-# validator.validate_request(content, None)
-# except ImmediateResponse, exc:
-# response = exc.response
-# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
-# else:
-# self.fail('ResourceException was not raised')
-
-# def validation_failed_due_to_invalid_field_returns_appropriate_message(self, validator):
-# """If validation fails due to an invalid field, ensure the response contains a single field error"""
-# content = {'qwerty': 'uiop', 'extra': 'extra'}
-# try:
-# validator.validate_request(content, None)
-# except ImmediateResponse, exc:
-# response = exc.response
-# self.assertEqual(response.raw_content, {'field_errors': {'extra': ['This field does not exist.']}})
-# else:
-# self.fail('ResourceException was not raised')
-
-# 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 ImmediateResponse, exc:
-# response = exc.response
-# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.'],
-# 'extra': ['This field does not exist.']}})
-# else:
-# self.fail('ResourceException was not raised')
-
-# # Tests on FormResource
-
-# def test_form_validation_returns_content_unchanged_if_already_valid_and_clean(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
-
-# def test_form_validation_failure_raises_response_exception(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_failure_raises_response_exception(validator)
-
-# def test_validation_does_not_allow_extra_fields_by_default(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_does_not_allow_extra_fields_by_default(validator)
-
-# def test_validation_allows_extra_fields_if_explicitly_set(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_allows_extra_fields_if_explicitly_set(validator)
-
-# def test_validation_allows_unknown_fields_if_explicitly_allowed(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_allows_unknown_fields_if_explicitly_allowed(validator)
-
-# def test_validation_does_not_require_extra_fields_if_explicitly_set(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
-
-# def test_validation_failed_due_to_no_content_returns_appropriate_message(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
-
-# def test_validation_failed_due_to_field_error_returns_appropriate_message(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
-
-# def test_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
-
-# def test_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
-# validator = self.MockFormResource(self.MockFormView())
-# self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
-
-# # Same tests on ModelResource
-
-# def test_modelform_validation_returns_content_unchanged_if_already_valid_and_clean(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
-
-# def test_modelform_validation_failure_raises_response_exception(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_failure_raises_response_exception(validator)
-
-# def test_modelform_validation_does_not_allow_extra_fields_by_default(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_does_not_allow_extra_fields_by_default(validator)
-
-# def test_modelform_validation_allows_extra_fields_if_explicitly_set(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_allows_extra_fields_if_explicitly_set(validator)
-
-# def test_modelform_validation_does_not_require_extra_fields_if_explicitly_set(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
-
-# def test_modelform_validation_failed_due_to_no_content_returns_appropriate_message(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
-
-# def test_modelform_validation_failed_due_to_field_error_returns_appropriate_message(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
-
-# def test_modelform_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
-
-# def test_modelform_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
-# validator = self.MockModelResource(self.MockModelFormView())
-# self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
-
-
-# class TestModelFormValidator(TestCase):
-# """Tests specific to ModelFormValidatorMixin"""
-
-# def setUp(self):
-# """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)
-
-# def test_property_fields_are_allowed_on_model_forms(self):
-# """Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
-# content = {'qwerty': 'example', 'uiop': 'example', 'readonly': 'read only'}
-# self.assertEqual(self.validator.validate_request(content, None), content)
-
-# def test_property_fields_are_not_required_on_model_forms(self):
-# """Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
-# content = {'qwerty': 'example', 'uiop': 'example'}
-# self.assertEqual(self.validator.validate_request(content, None), content)
-
-# def test_extra_fields_not_allowed_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 = {'qwerty': 'example', 'uiop': 'example', 'readonly': 'read only', 'extra': 'extra'}
-# self.assertRaises(ImmediateResponse, 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'}
-# self.assertRaises(ImmediateResponse, 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))
diff --git a/djangorestframework/tests/views.py b/djangorestframework/tests/views.py
deleted file mode 100644
index d4e4098a..00000000
--- a/djangorestframework/tests/views.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# from django.core.urlresolvers import reverse
-# from django.conf.urls.defaults import patterns, url, include
-# from django.http import HttpResponse
-# from django.test import TestCase
-# from django.utils import simplejson as json
-
-# from djangorestframework.views import View
-
-
-# class MockView(View):
-# """This is a basic mock view"""
-# pass
-
-
-# class MockViewFinal(View):
-# """View with final() override"""
-
-# def final(self, request, response, *args, **kwargs):
-# return HttpResponse('{"test": "passed"}', content_type="application/json")
-
-
-# # class ResourceMockView(View):
-# # """This is a resource-based mock view"""
-
-# # class MockForm(forms.Form):
-# # 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')
-
-# urlpatterns = patterns('',
-# url(r'^mock/$', MockView.as_view()),
-# url(r'^mock/final/$', MockViewFinal.as_view()),
-# # url(r'^resourcemock/$', ResourceMockView.as_view()),
-# # url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)),
-# # url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)),
-# url(r'^restframework/', include('djangorestframework.urls', namespace='djangorestframework')),
-# )
-
-
-# class BaseViewTests(TestCase):
-# """Test the base view class of djangorestframework"""
-# urls = 'djangorestframework.tests.views'
-
-# def test_view_call_final(self):
-# response = self.client.options('/mock/final/')
-# self.assertEqual(response['Content-Type'].split(';')[0], "application/json")
-# data = json.loads(response.content)
-# self.assertEqual(data['test'], 'passed')
-
-# def test_options_method_simple_view(self):
-# response = self.client.options('/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',
-# })
-
-# 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',
-# })
-
-# 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',
-# })
-
-# 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)
-# data = json.loads(response.content)
-# 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)
-
-
-# class ExtraViewsTests(TestCase):
-# """Test the extra views djangorestframework provides"""
-# urls = 'djangorestframework.tests.views'
-
-# def test_login_view(self):
-# """Ensure the login view exists"""
-# response = self.client.get(reverse('djangorestframework:login'))
-# self.assertEqual(response.status_code, 200)
-# self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
-
-# def test_logout_view(self):
-# """Ensure the logout view exists"""
-# response = self.client.get(reverse('djangorestframework:logout'))
-# self.assertEqual(response.status_code, 200)
-# self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
diff --git a/djangorestframework/throttling.py b/djangorestframework/throttling.py
deleted file mode 100644
index 6249bd42..00000000
--- a/djangorestframework/throttling.py
+++ /dev/null
@@ -1,217 +0,0 @@
-from django.core.cache import cache
-from djangorestframework.settings import api_settings
-import time
-
-
-class BaseThrottle(object):
- """
- Rate throttling of requests.
- """
-
- def __init__(self, view=None):
- """
- All throttles hold a reference to the instantiating view.
- """
- self.view = view
-
- def allow_request(self, request):
- """
- Return `True` if the request should be allowed, `False` otherwise.
- """
- raise NotImplementedError('.allow_request() must be overridden')
-
- def wait(self):
- """
- Optionally, return a recommeded number of seconds to wait before
- the next request.
- """
- return None
-
-
-class SimpleRateThottle(BaseThrottle):
- """
- A simple cache implementation, that only requires `.get_cache_key()`
- to be overridden.
-
- The rate (requests / seconds) is set by a :attr:`throttle` attribute
- on the :class:`.View` class. The attribute is a string of the form 'number of
- requests/period'.
-
- Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
-
- Previous request information used for throttling is stored in the cache.
- """
-
- timer = time.time
- settings = api_settings
- cache_format = 'throtte_%(scope)s_%(ident)s'
- scope = None
-
- def __init__(self, view):
- super(SimpleRateThottle, self).__init__(view)
- rate = self.get_rate_description()
- self.num_requests, self.duration = self.parse_rate_description(rate)
-
- def get_cache_key(self, request):
- """
- Should return a unique cache-key which can be used for throttling.
- Must be overridden.
-
- May return `None` if the request should not be throttled.
- """
- raise NotImplementedError('.get_cache_key() must be overridden')
-
- def get_rate_description(self):
- """
- Determine the string representation of the allowed request rate.
- """
- try:
- return self.rate
- except AttributeError:
- return self.settings.DEFAULT_THROTTLE_RATES.get(self.scope)
-
- def parse_rate_description(self, rate):
- """
- Given the request rate string, return a two tuple of:
- <allowed number of requests>, <period of time in seconds>
- """
- assert rate, "No throttle rate set for '%s'" % self.__class__.__name__
- num, period = rate.split('/')
- num_requests = int(num)
- duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
- return (num_requests, duration)
-
- def allow_request(self, request):
- """
- Implement the check to see if the request should be throttled.
-
- On success calls `throttle_success`.
- On failure calls `throttle_failure`.
- """
- self.key = self.get_cache_key(request)
- self.history = cache.get(self.key, [])
- self.now = self.timer()
-
- # Drop any requests from the history which have now passed the
- # throttle duration
- while self.history and self.history[-1] <= self.now - self.duration:
- self.history.pop()
- if len(self.history) >= self.num_requests:
- return self.throttle_failure()
- return self.throttle_success()
-
- def throttle_success(self):
- """
- Inserts the current request's timestamp along with the key
- into the cache.
- """
- self.history.insert(0, self.now)
- cache.set(self.key, self.history, self.duration)
- return True
-
- def throttle_failure(self):
- """
- Called when a request to the API has failed due to throttling.
- """
- return False
-
- def wait(self):
- """
- Returns the recommended next request time in seconds.
- """
- if self.history:
- remaining_duration = self.duration - (self.now - self.history[-1])
- else:
- remaining_duration = self.duration
-
- available_requests = self.num_requests - len(self.history) + 1
-
- return remaining_duration / float(available_requests)
-
-
-class AnonRateThrottle(SimpleRateThottle):
- """
- Limits the rate of API calls that may be made by a anonymous users.
-
- The IP address of the request will be used as the unqiue cache key.
- """
- scope = 'anon'
-
- def get_cache_key(self, request):
- if request.user.is_authenticated():
- return None # Only throttle unauthenticated requests.
-
- ident = request.META.get('REMOTE_ADDR', None)
-
- return self.cache_format % {
- 'scope': self.scope,
- 'ident': ident
- }
-
-
-class UserRateThrottle(SimpleRateThottle):
- """
- Limits the rate of API calls that may be made by a given user.
-
- The user id will be used as a unique cache key if the user is
- authenticated. For anonymous requests, the IP address of the request will
- be used.
- """
- scope = 'user'
-
- def get_cache_key(self, request):
- if request.user.is_authenticated():
- ident = request.user.id
- else:
- ident = request.META.get('REMOTE_ADDR', None)
-
- return self.cache_format % {
- 'scope': self.scope,
- 'ident': ident
- }
-
-
-class ScopedRateThrottle(SimpleRateThottle):
- """
- Limits the rate of API calls by different amounts for various parts of
- the API. Any view that has the `throttle_scope` property set will be
- throttled. The unique cache key will be generated by concatenating the
- user id of the request, and the scope of the view being accessed.
- """
-
- scope_attr = 'throttle_scope'
-
- def __init__(self, view):
- """
- Scope is determined from the view being accessed.
- """
- self.scope = getattr(self.view, self.scope_attr, None)
- super(ScopedRateThrottle, self).__init__(view)
-
- def parse_rate_description(self, rate):
- """
- Subclassed so that we don't fail if `view.throttle_scope` is not set.
- """
- if not rate:
- return (None, None)
- return super(ScopedRateThrottle, self).parse_rate_description(rate)
-
- def get_cache_key(self, request):
- """
- If `view.throttle_scope` is not set, don't apply this throttle.
-
- Otherwise generate the unique cache key by concatenating the user id
- with the '.throttle_scope` property of the view.
- """
- if not self.scope:
- return None # Only throttle views if `.throttle_scope` is set.
-
- if request.user.is_authenticated():
- ident = request.user.id
- else:
- ident = request.META.get('REMOTE_ADDR', None)
-
- return self.cache_format % {
- 'scope': self.scope,
- 'ident': ident
- }
diff --git a/djangorestframework/urlpatterns.py b/djangorestframework/urlpatterns.py
deleted file mode 100644
index 6cef721e..00000000
--- a/djangorestframework/urlpatterns.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from django.conf.urls.defaults import url
-from djangorestframework.settings import api_settings
-
-
-def format_suffix_patterns(urlpatterns, suffix_required=False, suffix_kwarg=None):
- """
- Supplement existing urlpatterns with corrosponding patterns that also
- include a '.format' suffix. Retains urlpattern ordering.
- """
- suffix_kwarg = suffix_kwarg or api_settings.FORMAT_SUFFIX_KWARG
- suffix_pattern = '.(?P<%s>[a-z]+)$' % suffix_kwarg
-
- ret = []
- for urlpattern in urlpatterns:
- # Form our complementing '.format' urlpattern
- regex = urlpattern.regex.pattern.rstrip('$') + suffix_pattern
- view = urlpattern._callback or urlpattern._callback_str
- kwargs = urlpattern.default_args
- name = urlpattern.name
- # Add in both the existing and the new urlpattern
- if not suffix_required:
- ret.append(urlpattern)
- ret.append(url(regex, view, kwargs, name))
- return ret
diff --git a/djangorestframework/urls.py b/djangorestframework/urls.py
deleted file mode 100644
index e446c396..00000000
--- a/djangorestframework/urls.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-Login and logout views for the browseable API.
-
-Add these to your root URLconf if you're using the browseable API and
-your API requires authentication.
-
-The urls must be namespaced as 'djangorestframework', and you should make sure
-your authentication settings include `SessionAuthentication`.
-
- urlpatterns = patterns('',
- ...
- url(r'^auth', include('djangorestframework.urls', namespace='djangorestframework'))
- )
-"""
-from django.conf.urls.defaults import patterns, url
-
-
-template_name = {'template_name': 'djangorestframework/login.html'}
-
-urlpatterns = patterns('django.contrib.auth.views',
- url(r'^login/$', 'login', template_name, name='login'),
- url(r'^logout/$', 'logout', template_name, name='logout'),
-)
diff --git a/djangorestframework/utils/__init__.py b/djangorestframework/utils/__init__.py
deleted file mode 100644
index bb5bb6d7..00000000
--- a/djangorestframework/utils/__init__.py
+++ /dev/null
@@ -1,139 +0,0 @@
-from django.utils.encoding import smart_unicode
-from django.utils.xmlutils import SimplerXMLGenerator
-
-from djangorestframework.compat import StringIO
-from djangorestframework.compat import RequestFactory as DjangoRequestFactory
-from djangorestframework.request import Request
-
-import re
-import xml.etree.ElementTree as ET
-
-
-# From xml2dict
-class XML2Dict(object):
-
- def __init__(self):
- pass
-
- def _parse_node(self, node):
- node_tree = {}
- # Save attrs and text, hope there will not be a child with same name
- if node.text:
- node_tree = node.text
- for (k, v) in node.attrib.items():
- k, v = self._namespace_split(k, v)
- node_tree[k] = v
- #Save childrens
- for child in node.getchildren():
- tag, tree = self._namespace_split(child.tag, self._parse_node(child))
- if tag not in node_tree: # the first time, so store it in dict
- node_tree[tag] = tree
- continue
- 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
-
- return node_tree
-
- def _namespace_split(self, tag, value):
- """
- Split the tag '{http://cs.sfsu.edu/csc867/myscheduler}patients'
- ns = http://cs.sfsu.edu/csc867/myscheduler
- name = patients
- """
- result = re.compile("\{(.*)\}(.*)").search(tag)
- if result:
- 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())
-
- def fromstring(self, s):
- """parse a string"""
- t = ET.fromstring(s)
- unused_root_tag, root_tree = self._namespace_split(t.tag, self._parse_node(t))
- return root_tree
-
-
-def xml2dict(input):
- return XML2Dict().fromstring(input)
-
-
-# Piston:
-class XMLRenderer():
- 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 data.iteritems():
- 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_unicode(data))
-
- def dict2xml(self, data):
- stream = StringIO.StringIO()
-
- xml = SimplerXMLGenerator(stream, "utf-8")
- xml.startDocument()
- xml.startElement("root", {})
-
- self._to_xml(xml, data)
-
- xml.endElement("root")
- xml.endDocument()
- return stream.getvalue()
-
-
-def dict2xml(input):
- return XMLRenderer().dict2xml(input)
-
-
-class RequestFactory(DjangoRequestFactory):
- """
- Replicate RequestFactory, but return Request, not HttpRequest.
- """
- def get(self, *args, **kwargs):
- parsers = kwargs.pop('parsers', None)
- request = super(RequestFactory, self).get(*args, **kwargs)
- return Request(request, parsers)
-
- def post(self, *args, **kwargs):
- parsers = kwargs.pop('parsers', None)
- request = super(RequestFactory, self).post(*args, **kwargs)
- return Request(request, parsers)
-
- def put(self, *args, **kwargs):
- parsers = kwargs.pop('parsers', None)
- request = super(RequestFactory, self).put(*args, **kwargs)
- return Request(request, parsers)
-
- def delete(self, *args, **kwargs):
- parsers = kwargs.pop('parsers', None)
- request = super(RequestFactory, self).delete(*args, **kwargs)
- return Request(request, parsers)
-
- def head(self, *args, **kwargs):
- parsers = kwargs.pop('parsers', None)
- request = super(RequestFactory, self).head(*args, **kwargs)
- return Request(request, parsers)
-
- def options(self, *args, **kwargs):
- parsers = kwargs.pop('parsers', None)
- request = super(RequestFactory, self).options(*args, **kwargs)
- return Request(request, parsers)
diff --git a/djangorestframework/utils/breadcrumbs.py b/djangorestframework/utils/breadcrumbs.py
deleted file mode 100644
index 84dd3dfa..00000000
--- a/djangorestframework/utils/breadcrumbs.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.core.urlresolvers import resolve
-
-
-def get_breadcrumbs(url):
- """Given a url returns a list of breadcrumbs, which are each a tuple of (name, url)."""
-
- from djangorestframework.views import APIView
-
- 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 Exception:
- pass
- else:
- # Check if this is a REST framework view, and if so add it to the breadcrumbs
- if isinstance(getattr(view, 'cls_instance', None), APIView):
- breadcrumbs_list.insert(0, (view.cls_instance.get_name(), 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)
-
- return breadcrumbs_recursive(url, [])
diff --git a/djangorestframework/utils/encoders.py b/djangorestframework/utils/encoders.py
deleted file mode 100644
index 74876017..00000000
--- a/djangorestframework/utils/encoders.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""
-Helper classes for parsers.
-"""
-import datetime
-import decimal
-from django.utils import simplejson as json
-from djangorestframework.compat import timezone
-
-
-class JSONEncoder(json.JSONEncoder):
- """
- JSONEncoder subclass that knows how to encode date/time,
- decimal types, and generators.
- """
- def default(self, o):
- # For Date Time string spec, see ECMA 262
- # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
- if isinstance(o, datetime.datetime):
- r = o.isoformat()
- if o.microsecond:
- r = r[:23] + r[26:]
- if r.endswith('+00:00'):
- r = r[:-6] + 'Z'
- return r
- elif isinstance(o, datetime.date):
- return o.isoformat()
- elif isinstance(o, datetime.time):
- if timezone and timezone.is_aware(o):
- raise ValueError("JSON can't represent timezone-aware times.")
- r = o.isoformat()
- if o.microsecond:
- r = r[:12]
- return r
- elif isinstance(o, decimal.Decimal):
- return str(o)
- elif hasattr(o, '__iter__'):
- return [i for i in o]
- return super(JSONEncoder, self).default(o)
diff --git a/djangorestframework/utils/mediatypes.py b/djangorestframework/utils/mediatypes.py
deleted file mode 100644
index 5eba7fb2..00000000
--- a/djangorestframework/utils/mediatypes.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""
-Handling of media types, as found in HTTP Content-Type and Accept headers.
-
-See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
-"""
-
-from django.http.multipartparser import parse_header
-
-
-def media_type_matches(lhs, rhs):
- """
- Returns ``True`` if the media type in the first argument <= the
- media type in the second argument. The media types are strings
- as described by the HTTP spec.
-
- Valid media type strings include:
-
- 'application/json; indent=4'
- 'application/json'
- 'text/*'
- '*/*'
- """
- lhs = _MediaType(lhs)
- rhs = _MediaType(rhs)
- return lhs.match(rhs)
-
-
-def is_form_media_type(media_type):
- """
- Return True if the media type is a valid form media type as defined by the HTML4 spec.
- (NB. HTML5 also adds text/plain to the list of valid form media types, but we don't support this here)
- """
- media_type = _MediaType(media_type)
- return media_type.full_type == 'application/x-www-form-urlencoded' or \
- media_type.full_type == 'multipart/form-data'
-
-
-def add_media_type_param(media_type, key, val):
- """
- Add a key, value parameter to a media type string, and return the new media type string.
- """
- media_type = _MediaType(media_type)
- media_type.params[key] = val
- return str(media_type)
-
-
-def get_media_type_params(media_type):
- """
- Return a dictionary of the parameters on the given media type.
- """
- return _MediaType(media_type).params
-
-
-def order_by_precedence(media_type_lst):
- """
- Returns a list of sets of media type strings, ordered by precedence.
- Precedence is determined by how specific a media type is:
-
- 3. 'type/subtype; param=val'
- 2. 'type/subtype'
- 1. 'type/*'
- 0. '*/*'
- """
- ret = [set(), set(), set(), set()]
- for media_type in media_type_lst:
- precedence = _MediaType(media_type).precedence
- ret[3 - precedence].add(media_type)
- return [media_types for media_types in ret if media_types]
-
-
-class _MediaType(object):
- def __init__(self, media_type_str):
- if media_type_str is None:
- media_type_str = ''
- self.orig = media_type_str
- self.full_type, self.params = parse_header(media_type_str)
- self.main_type, sep, self.sub_type = self.full_type.partition('/')
-
- def match(self, other):
- """Return true if this MediaType satisfies the given MediaType."""
- for key in self.params.keys():
- if key != 'q' and other.params.get(key, None) != self.params.get(key, None):
- return False
-
- if self.sub_type != '*' and other.sub_type != '*' and other.sub_type != self.sub_type:
- return False
-
- if self.main_type != '*' and other.main_type != '*' and other.main_type != self.main_type:
- return False
-
- return True
-
- @property
- def precedence(self):
- """
- Return a precedence level from 0-3 for the media type given how specific it is.
- """
- if self.main_type == '*':
- return 0
- elif self.sub_type == '*':
- return 1
- elif not self.params or self.params.keys() == ['q']:
- return 2
- return 3
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- def __unicode__(self):
- ret = "%s/%s" % (self.main_type, self.sub_type)
- for key, val in self.params.items():
- ret += "; %s=%s" % (key, val)
- return ret
diff --git a/djangorestframework/views.py b/djangorestframework/views.py
deleted file mode 100644
index a309386b..00000000
--- a/djangorestframework/views.py
+++ /dev/null
@@ -1,282 +0,0 @@
-"""
-The :mod:`views` module provides the Views you will most probably
-be subclassing in your implementation.
-
-By setting or modifying class attributes on your view, you change it's predefined behaviour.
-"""
-
-import re
-from django.core.exceptions import PermissionDenied
-from django.http import Http404
-from django.utils.html import escape
-from django.utils.safestring import mark_safe
-from django.views.decorators.csrf import csrf_exempt
-
-from djangorestframework.compat import View as _View, apply_markdown
-from djangorestframework.response import Response
-from djangorestframework.request import Request
-from djangorestframework.settings import api_settings
-from djangorestframework import status, exceptions
-
-
-def _remove_trailing_string(content, trailing):
- """
- Strip trailing component `trailing` from `content` if it exists.
- Used when generating names from view classes.
- """
- if content.endswith(trailing) and content != trailing:
- return content[:-len(trailing)]
- return content
-
-
-def _remove_leading_indent(content):
- """
- Remove leading indent from a block of text.
- Used when generating descriptions from docstrings.
- """
- whitespace_counts = [len(line) - len(line.lstrip(' '))
- for line in content.splitlines()[1:] if line.lstrip()]
-
- # unindent the content if needed
- if whitespace_counts:
- whitespace_pattern = '^' + (' ' * min(whitespace_counts))
- return re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
- return content
-
-
-def _camelcase_to_spaces(content):
- """
- Translate 'CamelCaseNames' to 'Camel Case Names'.
- Used when generating names from view classes.
- """
- camelcase_boundry = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'
- return re.sub(camelcase_boundry, ' \\1', content).strip()
-
-
-class APIView(_View):
- renderer_classes = api_settings.DEFAULT_RENDERERS
- parser_classes = api_settings.DEFAULT_PARSERS
- authentication_classes = api_settings.DEFAULT_AUTHENTICATION
- throttle_classes = api_settings.DEFAULT_THROTTLES
- permission_classes = api_settings.DEFAULT_PERMISSIONS
-
- @classmethod
- def as_view(cls, **initkwargs):
- """
- Override the default :meth:`as_view` to store an instance of the view
- as an attribute on the callable function. This allows us to discover
- information about the view when we do URL reverse lookups.
- """
- view = super(APIView, cls).as_view(**initkwargs)
- view.cls_instance = cls(**initkwargs)
- return view
-
- @property
- def allowed_methods(self):
- """
- Return the list of allowed HTTP methods, uppercased.
- """
- return [method.upper() for method in self.http_method_names
- if hasattr(self, method)]
-
- @property
- def default_response_headers(self):
- return {
- 'Allow': ', '.join(self.allowed_methods),
- 'Vary': 'Authenticate, Accept'
- }
-
- def get_name(self):
- """
- Return the resource or view class name for use as this view's name.
- Override to customize.
- """
- name = self.__class__.__name__
- name = _remove_trailing_string(name, 'View')
- return _camelcase_to_spaces(name)
-
- def get_description(self, html=False):
- """
- Return the resource or view docstring for use as this view's description.
- Override to customize.
- """
- description = self.__doc__ or ''
- description = _remove_leading_indent(description)
- if html:
- return self.markup_description(description)
- return description
-
- def markup_description(self, description):
- """
- Apply HTML markup to the description of this view.
- """
- if apply_markdown:
- description = apply_markdown(description)
- else:
- description = escape(description).replace('\n', '<br />')
- return mark_safe(description)
-
- def http_method_not_allowed(self, request, *args, **kwargs):
- """
- Called if `request.method` does not corrospond to a handler method.
- """
- raise exceptions.MethodNotAllowed(request.method)
-
- def permission_denied(self, request):
- """
- If request is not permitted, determine what kind of exception to raise.
- """
- raise exceptions.PermissionDenied()
-
- def throttled(self, request, wait):
- """
- If request is throttled, determine what kind of exception to raise.
- """
- raise exceptions.Throttled(wait)
-
- @property
- def _parsed_media_types(self):
- """
- Return a list of all the media types that this view can parse.
- """
- return [parser.media_type for parser in self.parser_classes]
-
- @property
- def _default_parser(self):
- """
- Return the view's default parser class.
- """
- return self.parser_classes[0]
-
- @property
- def _rendered_media_types(self):
- """
- Return an list of all the media types that this response can render.
- """
- return [renderer.media_type for renderer in self.renderer_classes]
-
- @property
- def _rendered_formats(self):
- """
- Return a list of all the formats that this response can render.
- """
- return [renderer.format for renderer in self.renderer_classes]
-
- @property
- def _default_renderer(self):
- """
- Return the response's default renderer class.
- """
- return self.renderer_classes[0]
-
- def get_permissions(self):
- """
- Instantiates and returns the list of permissions that this view requires.
- """
- return [permission(self) for permission in self.permission_classes]
-
- def get_throttles(self):
- """
- Instantiates and returns the list of thottles that this view requires.
- """
- return [throttle(self) for throttle in self.throttle_classes]
-
- def check_permissions(self, request, obj=None):
- """
- Check if request should be permitted.
- """
- for permission in self.get_permissions():
- if not permission.has_permission(request, obj):
- self.permission_denied(request)
-
- def check_throttles(self, request):
- """
- Check if request should be throttled.
- """
- for throttle in self.get_throttles():
- if not throttle.allow_request(request):
- self.throttled(request, throttle.wait())
-
- def initialize_request(self, request, *args, **kargs):
- """
- Returns the initial request object.
- """
- return Request(request, parser_classes=self.parser_classes, authentication_classes=self.authentication_classes)
-
- def finalize_response(self, request, response, *args, **kwargs):
- """
- Returns the final response object.
- """
- if isinstance(response, Response):
- response.view = self
- response.request = request
- response.renderer_classes = self.renderer_classes
- if api_settings.FORMAT_SUFFIX_KWARG:
- response.format = kwargs.get(api_settings.FORMAT_SUFFIX_KWARG, None)
-
- for key, value in self.headers.items():
- response[key] = value
-
- return response
-
- def initial(self, request, *args, **kwargs):
- """
- Runs anything that needs to occur prior to calling the method handlers.
- """
- self.check_permissions(request)
- self.check_throttles(request)
-
- def handle_exception(self, exc):
- """
- Handle any exception that occurs, by returning an appropriate response,
- or re-raising the error.
- """
- if isinstance(exc, exceptions.Throttled):
- self.headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait
-
- if isinstance(exc, exceptions.APIException):
- return Response({'detail': exc.detail}, status=exc.status_code)
- elif isinstance(exc, Http404):
- return Response({'detail': 'Not found'},
- status=status.HTTP_404_NOT_FOUND)
- elif isinstance(exc, PermissionDenied):
- return Response({'detail': 'Permission denied'},
- status=status.HTTP_403_FORBIDDEN)
- raise
-
- # Note: session based authentication is explicitly CSRF validated,
- # all other authentication is CSRF exempt.
- @csrf_exempt
- def dispatch(self, request, *args, **kwargs):
- """
- `APIView.dispatch()` is pretty much the same as Django's regular
- `View.dispatch()`, except that it includes hooks to:
-
- * Initialize the request object.
- * Finalize the response object.
- * Handle exceptions that occur in the handler method.
- * An initial hook for code such as permission checking that should
- occur prior to running the method handlers.
- """
- request = self.initialize_request(request, *args, **kwargs)
- self.request = request
- self.args = args
- self.kwargs = kwargs
- self.headers = self.default_response_headers
-
- try:
- self.initial(request, *args, **kwargs)
-
- # Get the appropriate handler method
- if request.method.lower() in self.http_method_names:
- handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
- else:
- handler = self.http_method_not_allowed
-
- response = handler(request, *args, **kwargs)
-
- except Exception as exc:
- response = self.handle_exception(exc)
-
- self.response = self.finalize_response(request, response, *args, **kwargs)
- return self.response