Drop 1.3 support

This commit is contained in:
Tom Christie 2013-09-25 10:30:04 +01:00
parent 21cd638659
commit e441f85109
28 changed files with 56 additions and 506 deletions

View File

@ -10,18 +10,15 @@ env:
- DJANGO="https://www.djangoproject.com/download/1.6a1/tarball/" - DJANGO="https://www.djangoproject.com/download/1.6a1/tarball/"
- DJANGO="django==1.5.1 --use-mirrors" - DJANGO="django==1.5.1 --use-mirrors"
- DJANGO="django==1.4.5 --use-mirrors" - DJANGO="django==1.4.5 --use-mirrors"
- DJANGO="django==1.3.7 --use-mirrors"
install: install:
- pip install $DJANGO - pip install $DJANGO
- pip install defusedxml==0.3 - pip install defusedxml==0.3 --use-mirrors
- pip install django-filter==0.6 --use-mirrors
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.0 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.0 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.4 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.4 --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-guardian==1.1.1 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-guardian==1.1.1 --use-mirrors; fi"
- "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi"
- "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install six --use-mirrors; fi"
- "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.6 --use-mirrors; fi"
- export PYTHONPATH=. - export PYTHONPATH=.
script: script:
@ -31,10 +28,5 @@ matrix:
exclude: exclude:
- python: "3.2" - python: "3.2"
env: DJANGO="django==1.4.5 --use-mirrors" env: DJANGO="django==1.4.5 --use-mirrors"
- python: "3.2"
env: DJANGO="django==1.3.7 --use-mirrors"
- python: "3.3" - python: "3.3"
env: DJANGO="django==1.4.5 --use-mirrors" env: DJANGO="django==1.4.5 --use-mirrors"
- python: "3.3"
env: DJANGO="django==1.3.7 --use-mirrors"

View File

@ -6,8 +6,8 @@ import base64
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.middleware.csrf import CsrfViewMiddleware
from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework import exceptions, HTTP_HEADER_ENCODING
from rest_framework.compat import CsrfViewMiddleware
from rest_framework.compat import oauth, oauth_provider, oauth_provider_store from rest_framework.compat import oauth, oauth_provider, oauth_provider_store
from rest_framework.compat import oauth2_provider, provider_now from rest_framework.compat import oauth2_provider, provider_now
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token

View File

@ -5,25 +5,19 @@ versions of django/python, and compatibility wrappers around optional packages.
# flake8: noqa # flake8: noqa
from __future__ import unicode_literals from __future__ import unicode_literals
import django import django
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.conf import settings from django.conf import settings
# Try to import six from Django, fallback to external `six` package. # Try to import six from Django, fallback to external `six` package.
try: try:
from django.utils import six from django.utils import six
except ImportError: except ImportError:
import six import six
# location of patterns, url, include changes in 1.4 onwards # Handle django.utils.encoding rename in 1.5 onwards.
try: # smart_unicode -> smart_text
from django.conf.urls import patterns, url, include
except ImportError:
from django.conf.urls.defaults import patterns, url, include
# Handle django.utils.encoding rename:
# smart_unicode -> smart_text
# force_unicode -> force_text # force_unicode -> force_text
try: try:
from django.utils.encoding import smart_text from django.utils.encoding import smart_text
@ -41,13 +35,15 @@ try:
except ImportError: except ImportError:
from django.http import HttpResponse as HttpResponseBase from django.http import HttpResponse as HttpResponseBase
# django-filter is optional # django-filter is optional
try: try:
import django_filters import django_filters
except ImportError: except ImportError:
django_filters = None django_filters = None
# guardian is optional
# django-guardian is optional
try: try:
import guardian import guardian
except ImportError: except ImportError:
@ -80,14 +76,6 @@ except ImportError:
Image = None Image = None
def get_concrete_model(model_cls):
try:
return model_cls._meta.concrete_model
except AttributeError:
# 1.3 does not include concrete model
return model_cls
# Django 1.5 add support for custom auth user model # Django 1.5 add support for custom auth user model
if django.VERSION >= (1, 5): if django.VERSION >= (1, 5):
AUTH_USER_MODEL = settings.AUTH_USER_MODEL AUTH_USER_MODEL = settings.AUTH_USER_MODEL
@ -95,46 +83,13 @@ else:
AUTH_USER_MODEL = 'auth.User' AUTH_USER_MODEL = 'auth.User'
# View._allowed_methods only present from 1.5 onwards
if django.VERSION >= (1, 5): if django.VERSION >= (1, 5):
from django.views.generic import View from django.views.generic import View
else: else:
from django.views.generic import View as _View from django.views.generic import View as DjangoView
from django.utils.decorators import classonlymethod
from django.utils.functional import update_wrapper
class View(_View): class View(DjangoView):
# 1.3 does not include head method in base View class
# See: https://code.djangoproject.com/ticket/15668
@classonlymethod
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("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
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
# _allowed_methods only present from 1.5 onwards
def _allowed_methods(self): def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)] return [m.upper() for m in self.http_method_names if hasattr(self, m)]
@ -144,316 +99,16 @@ if 'patch' not in View.http_method_names:
View.http_method_names = View.http_method_names + ['patch'] View.http_method_names = View.http_method_names + ['patch']
# PUT, DELETE do not require CSRF until 1.4. They should. Make it better. # RequestFactory only provides `generic` from 1.5 onwards
if django.VERSION >= (1, 4):
from django.middleware.csrf import CsrfViewMiddleware
else:
import hashlib
import re
import random
import logging
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 = 18446744073709551616 # 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, parse_time
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)
# smart_urlquote is new on Django 1.4
try:
from django.utils.html import smart_urlquote
except ImportError:
import re
from django.utils.encoding import smart_str
try:
from urllib.parse import quote, urlsplit, urlunsplit
except ImportError: # Python 2
from urllib import quote
from urlparse import urlsplit, urlunsplit
unquoted_percents_re = re.compile(r'%(?![0-9A-Fa-f]{2})')
def smart_urlquote(url):
"Quotes a URL if it isn't already quoted."
# Handle IDN before quoting.
scheme, netloc, path, query, fragment = urlsplit(url)
try:
netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE
except UnicodeError: # invalid domain part
pass
else:
url = urlunsplit((scheme, netloc, path, query, fragment))
# An URL is considered unquoted if it contains no % characters or
# contains a % not followed by two hexadecimal digits. See #9655.
if '%' not in url or unquoted_percents_re.search(url):
# See http://bugs.python.org/issue2637
url = quote(smart_str(url), safe=b'!*\'();:@&=+$,/?#[]~')
return force_text(url)
# RequestFactory only provide `generic` from 1.5 onwards
from django.test.client import RequestFactory as DjangoRequestFactory from django.test.client import RequestFactory as DjangoRequestFactory
from django.test.client import FakePayload from django.test.client import FakePayload
try: try:
# In 1.5 the test client uses force_bytes # In 1.5 the test client uses force_bytes
from django.utils.encoding import force_bytes_or_smart_bytes from django.utils.encoding import force_bytes_or_smart_bytes
except ImportError: except ImportError:
# In 1.3 and 1.4 the test client just uses smart_str # In 1.4 the test client just uses smart_str
from django.utils.encoding import smart_str as force_bytes_or_smart_bytes from django.utils.encoding import smart_str as force_bytes_or_smart_bytes
class RequestFactory(DjangoRequestFactory): class RequestFactory(DjangoRequestFactory):
def generic(self, method, path, def generic(self, method, path,
data='', content_type='application/octet-stream', **extra): data='', content_type='application/octet-stream', **extra):
@ -478,6 +133,7 @@ class RequestFactory(DjangoRequestFactory):
r.update(extra) r.update(extra)
return self.request(**r) return self.request(**r)
# Markdown is optional # Markdown is optional
try: try:
import markdown import markdown
@ -492,7 +148,6 @@ try:
safe_mode = False safe_mode = False
md = markdown.Markdown(extensions=extensions, safe_mode=safe_mode) md = markdown.Markdown(extensions=extensions, safe_mode=safe_mode)
return md.convert(text) return md.convert(text)
except ImportError: except ImportError:
apply_markdown = None apply_markdown = None
@ -510,14 +165,16 @@ try:
except ImportError: except ImportError:
etree = None etree = None
# OAuth is optional
# OAuth2 is optional
try: try:
# Note: The `oauth2` package actually provides oauth1.0a support. Urg. # Note: The `oauth2` package actually provides oauth1.0a support. Urg.
import oauth2 as oauth import oauth2 as oauth
except ImportError: except ImportError:
oauth = None oauth = None
# OAuth is optional
# OAuthProvider is optional
try: try:
import oauth_provider import oauth_provider
from oauth_provider.store import store as oauth_provider_store from oauth_provider.store import store as oauth_provider_store
@ -525,6 +182,7 @@ except (ImportError, ImproperlyConfigured):
oauth_provider = None oauth_provider = None
oauth_provider_store = None oauth_provider_store = None
# OAuth 2 support is optional # OAuth 2 support is optional
try: try:
import provider.oauth2 as oauth2_provider import provider.oauth2 as oauth2_provider
@ -542,8 +200,6 @@ try:
# Any other supported version does use timezone aware datetimes # Any other supported version does use timezone aware datetimes
from django.utils.timezone import now as provider_now from django.utils.timezone import now as provider_now
except ImportError: except ImportError:
import traceback
traceback.print_exc()
oauth2_provider = None oauth2_provider = None
oauth2_provider_models = None oauth2_provider_models = None
oauth2_provider_forms = None oauth2_provider_forms = None
@ -551,7 +207,8 @@ except ImportError:
oauth2_constants = None oauth2_constants = None
provider_now = None provider_now = None
# Handle lazy strings
# Handle lazy strings across Py2/Py3
from django.utils.functional import Promise from django.utils.functional import Promise
if six.PY3: if six.PY3:

View File

@ -18,12 +18,14 @@ from django.conf import settings
from django.db.models.fields import BLANK_CHOICE_DASH from django.db.models.fields import BLANK_CHOICE_DASH
from django.http import QueryDict from django.http import QueryDict
from django.forms import widgets from django.forms import widgets
from django.utils import timezone
from django.utils.encoding import is_protected_type from django.utils.encoding import is_protected_type
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from rest_framework import ISO_8601 from rest_framework import ISO_8601
from rest_framework.compat import ( from rest_framework.compat import (
timezone, parse_date, parse_datetime, parse_time, BytesIO, six, smart_text, BytesIO, six, smart_text,
force_text, is_non_str_iterable force_text, is_non_str_iterable
) )
from rest_framework.settings import api_settings from rest_framework.settings import api_settings

View File

@ -17,9 +17,9 @@ from __future__ import unicode_literals
import itertools import itertools
from collections import namedtuple from collections import namedtuple
from django.conf.urls import patterns, url
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from rest_framework import views from rest_framework import views
from rest_framework.compat import patterns, url
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.urlpatterns import format_suffix_patterns from rest_framework.urlpatterns import format_suffix_patterns

View File

@ -93,10 +93,7 @@ INSTALLED_APPS = (
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.sites', 'django.contrib.sites',
'django.contrib.messages', 'django.contrib.messages',
# Uncomment the next line to enable the admin: 'django.contrib.staticfiles',
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'rest_framework.tests', 'rest_framework.tests',

View File

@ -1,7 +1,7 @@
""" """
Blank URLConf just to keep runtests.py happy. Blank URLConf just to keep runtests.py happy.
""" """
from rest_framework.compat import patterns from django.conf.urls import patterns
urlpatterns = patterns('', urlpatterns = patterns('',
) )

View File

@ -18,7 +18,7 @@ from decimal import Decimal
from django.db import models from django.db import models
from django.forms import widgets from django.forms import widgets
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from rest_framework.compat import get_concrete_model, six from rest_framework.compat import six
# Note: We do the following so that users of the framework can use this style: # Note: We do the following so that users of the framework can use this style:
# #
@ -575,7 +575,7 @@ class ModelSerializer(Serializer):
cls = self.opts.model cls = self.opts.model
assert cls is not None, \ assert cls is not None, \
"Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__ "Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__
opts = get_concrete_model(cls)._meta opts = cls._meta.concrete_model._meta
ret = SortedDict() ret = SortedDict()
nested = bool(self.opts.depth) nested = bool(self.opts.depth)
@ -784,7 +784,7 @@ class ModelSerializer(Serializer):
Return a list of field names to exclude from model validation. Return a list of field names to exclude from model validation.
""" """
cls = self.opts.model cls = self.opts.model
opts = get_concrete_model(cls)._meta opts = cls._meta.concrete_model._meta
exclusions = [field.name for field in opts.fields + opts.many_to_many] exclusions = [field.name for field in opts.fields + opts.many_to_many]
for field_name, field in self.fields.items(): for field_name, field in self.fields.items():

View File

@ -1,4 +1,5 @@
{% load url from future %} {% load url from future %}
{% load staticfiles %}
{% load rest_framework %} {% load rest_framework %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>

View File

@ -1,4 +1,5 @@
{% load url from future %} {% load url from future %}
{% load staticfiles %}
{% load rest_framework %} {% load rest_framework %}
<html> <html>

View File

@ -2,97 +2,14 @@ from __future__ import unicode_literals, absolute_import
from django import template from django import template
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import QueryDict from django.http import QueryDict
from django.utils.html import escape from django.utils.html import escape, smart_urlquote
from django.utils.safestring import SafeData, mark_safe from django.utils.safestring import SafeData, mark_safe
from rest_framework.compat import urlparse, force_text, six, smart_urlquote from rest_framework.compat import urlparse, force_text, six
import re, string import re, string
register = template.Library() register = template.Library()
# Note we don't use 'load staticfiles', because we need a 1.3 compatible
# version, so instead we include the `static` template tag ourselves.
# When 1.3 becomes unsupported by REST framework, we can instead start to
# use the {% load staticfiles %} tag, remove the following code,
# and add a dependency that `django.contrib.staticfiles` must be installed.
# Note: We can't put this into the `compat` module because the compat import
# from rest_framework.compat import ...
# conflicts with this rest_framework template tag module.
try: # Django 1.5+
from django.contrib.staticfiles.templatetags.staticfiles import StaticFilesNode
@register.tag('static')
def do_static(parser, token):
return StaticFilesNode.handle_token(parser, token)
except ImportError:
try: # Django 1.4
from django.contrib.staticfiles.storage import staticfiles_storage
@register.simple_tag
def static(path):
"""
A template tag that returns the URL to a file
using staticfiles' storage backend
"""
return staticfiles_storage.url(path)
except ImportError: # Django 1.3
from urlparse import urljoin
from django import template
from django.templatetags.static import PrefixNode
class StaticNode(template.Node):
def __init__(self, varname=None, path=None):
if path is None:
raise template.TemplateSyntaxError(
"Static template nodes must be given a path to return.")
self.path = path
self.varname = varname
def url(self, context):
path = self.path.resolve(context)
return self.handle_simple(path)
def render(self, context):
url = self.url(context)
if self.varname is None:
return url
context[self.varname] = url
return ''
@classmethod
def handle_simple(cls, path):
return urljoin(PrefixNode.handle_simple("STATIC_URL"), path)
@classmethod
def handle_token(cls, parser, token):
"""
Class method to parse prefix node and return a Node.
"""
bits = token.split_contents()
if len(bits) < 2:
raise template.TemplateSyntaxError(
"'%s' takes at least one argument (path to file)" % bits[0])
path = parser.compile_filter(bits[1])
if len(bits) >= 2 and bits[-2] == 'as':
varname = bits[3]
else:
varname = None
return cls(varname, path)
@register.tag('static')
def do_static_13(parser, token):
return StaticNode.handle_token(parser, token)
def replace_query_param(url, key, val): def replace_query_param(url, key, val):
""" """
Given a URL and a key/val pair, set or replace an item in the query Given a URL and a key/val pair, set or replace an item in the query

View File

@ -1,4 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url, include
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpResponse from django.http import HttpResponse
from django.test import TestCase from django.test import TestCase
@ -18,7 +19,6 @@ from rest_framework.authentication import (
OAuth2Authentication OAuth2Authentication
) )
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.compat import patterns, url, include
from rest_framework.compat import oauth2_provider, oauth2_provider_models, oauth2_provider_scope from rest_framework.compat import oauth2_provider, oauth2_provider_models, oauth2_provider_scope
from rest_framework.compat import oauth, oauth_provider from rest_framework.compat import oauth, oauth_provider
from rest_framework.test import APIRequestFactory, APIClient from rest_framework.test import APIRequestFactory, APIClient

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url
from django.test import TestCase from django.test import TestCase
from rest_framework.compat import patterns, url
from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.utils.breadcrumbs import get_breadcrumbs
from rest_framework.views import APIView from rest_framework.views import APIView

View File

@ -1,12 +1,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
from decimal import Decimal from decimal import Decimal
from django.conf.urls import patterns, url
from django.db import models from django.db import models
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import TestCase from django.test import TestCase
from django.utils import unittest from django.utils import unittest
from rest_framework import generics, serializers, status, filters from rest_framework import generics, serializers, status, filters
from rest_framework.compat import django_filters, patterns, url from rest_framework.compat import django_filters
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from rest_framework.tests.models import BasicModel from rest_framework.tests.models import BasicModel

View File

@ -1,11 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.conf.urls import patterns, url
from django.http import Http404 from django.http import Http404
from django.test import TestCase from django.test import TestCase
from django.template import TemplateDoesNotExist, Template from django.template import TemplateDoesNotExist, Template
import django.template.loader import django.template.loader
from rest_framework import status from rest_framework import status
from rest_framework.compat import patterns, url
from rest_framework.decorators import api_view, renderer_classes from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.response import Response from rest_framework.response import Response

View File

@ -1,8 +1,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import json import json
from django.conf.urls import patterns, url
from django.test import TestCase from django.test import TestCase
from rest_framework import generics, status, serializers from rest_framework import generics, status, serializers
from rest_framework.compat import patterns, url
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from rest_framework.tests.models import ( from rest_framework.tests.models import (
Anchor, BasicModel, ManyToManyModel, BlogPost, BlogPostComment, Anchor, BasicModel, ManyToManyModel, BlogPost, BlogPostComment,
@ -24,7 +24,7 @@ class BlogPostCommentSerializer(serializers.ModelSerializer):
class PhotoSerializer(serializers.Serializer): class PhotoSerializer(serializers.Serializer):
description = serializers.CharField() description = serializers.CharField()
album_url = serializers.HyperlinkedRelatedField(source='album', view_name='album-detail', queryset=Album.objects.all(), lookup_field='title', slug_url_kwarg='title') album_url = serializers.HyperlinkedRelatedField(source='album', view_name='album-detail', queryset=Album.objects.all(), lookup_field='title')
def restore_object(self, attrs, instance=None): def restore_object(self, attrs, instance=None):
return Photo(**attrs) return Photo(**attrs)

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url
from django.test import TestCase from django.test import TestCase
from rest_framework import serializers from rest_framework import serializers
from rest_framework.compat import patterns, url
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from rest_framework.tests.models import ( from rest_framework.tests.models import (
BlogPost, BlogPost,

View File

@ -2,12 +2,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from decimal import Decimal from decimal import Decimal
from django.conf.urls import patterns, url, include
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase from django.test import TestCase
from django.utils import unittest from django.utils import unittest
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import status, permissions from rest_framework import status, permissions
from rest_framework.compat import yaml, etree, patterns, url, include, six, StringIO from rest_framework.compat import yaml, etree, six, StringIO
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \ from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \

View File

@ -2,13 +2,13 @@
Tests for content parsing, and form-overloaded content parsing. Tests for content parsing, and form-overloaded content parsing.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase from django.test import TestCase
from rest_framework import status from rest_framework import status
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from rest_framework.compat import patterns
from rest_framework.parsers import ( from rest_framework.parsers import (
BaseParser, BaseParser,
FormParser, FormParser,

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url, include
from django.test import TestCase from django.test import TestCase
from rest_framework.tests.models import BasicModel, BasicModelSerializer from rest_framework.tests.models import BasicModel, BasicModelSerializer
from rest_framework.compat import patterns, url, include
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework import generics from rest_framework import generics

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url
from django.test import TestCase from django.test import TestCase
from rest_framework.compat import patterns, url
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory

View File

@ -1,9 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url, include
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from rest_framework import serializers, viewsets, permissions from rest_framework import serializers, viewsets, permissions
from rest_framework.compat import include, patterns, url
from rest_framework.decorators import detail_route, list_route from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.routers import SimpleRouter, DefaultRouter from rest_framework.routers import SimpleRouter, DefaultRouter

View File

@ -1,9 +1,9 @@
# -- coding: utf-8 -- # -- coding: utf-8 --
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from rest_framework.compat import patterns, url
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.test import APIClient, APIRequestFactory, force_authenticate from rest_framework.test import APIClient, APIRequestFactory, force_authenticate

View File

@ -1,9 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from collections import namedtuple from collections import namedtuple
from django.conf.urls import patterns, url, include
from django.core import urlresolvers from django.core import urlresolvers
from django.test import TestCase from django.test import TestCase
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from rest_framework.compat import patterns, url, include
from rest_framework.urlpatterns import format_suffix_patterns from rest_framework.urlpatterns import format_suffix_patterns

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url, include
from django.core.urlresolvers import RegexURLResolver from django.core.urlresolvers import RegexURLResolver
from rest_framework.compat import url, include
from rest_framework.settings import api_settings from rest_framework.settings import api_settings

View File

@ -13,7 +13,7 @@ your authentication settings include `SessionAuthentication`.
) )
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from rest_framework.compat import patterns, url from django.conf.urls import patterns, url
template_name = {'template_name': 'rest_framework/login.html'} template_name = {'template_name': 'rest_framework/login.html'}

View File

@ -2,9 +2,10 @@
Helper classes for parsers. Helper classes for parsers.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils import timezone
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.functional import Promise from django.utils.functional import Promise
from rest_framework.compat import timezone, force_text from rest_framework.compat import force_text
from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata
import datetime import datetime
import decimal import decimal

22
tox.ini
View File

@ -1,6 +1,6 @@
[tox] [tox]
downloadcache = {toxworkdir}/cache/ downloadcache = {toxworkdir}/cache/
envlist = py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,py2.7-django1.4,py2.6-django1.4,py2.7-django1.3,py2.6-django1.3 envlist = py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,py2.7-django1.4,py2.6-django1.4
[testenv] [testenv]
commands = {envpython} rest_framework/runtests/runtests.py commands = {envpython} rest_framework/runtests/runtests.py
@ -88,23 +88,3 @@ deps = django==1.4.3
oauth2==1.5.211 oauth2==1.5.211
django-oauth2-provider==0.2.3 django-oauth2-provider==0.2.3
django-guardian==1.1.1 django-guardian==1.1.1
[testenv:py2.7-django1.3]
basepython = python2.7
deps = django==1.3.5
django-filter==0.5.4
defusedxml==0.3
django-oauth-plus==2.0
oauth2==1.5.211
django-oauth2-provider==0.2.3
django-guardian==1.1.1
[testenv:py2.6-django1.3]
basepython = python2.6
deps = django==1.3.5
django-filter==0.5.4
defusedxml==0.3
django-oauth-plus==2.0
oauth2==1.5.211
django-oauth2-provider==0.2.3
django-guardian==1.1.1