Skip to content

Fix 1.10 deprecation warnings #4505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/api-guide/reverse.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ There's no requirement for you to use them, but if you do then the self-describi

**Signature:** `reverse(viewname, *args, **kwargs)`

Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port.
Has the same behavior as [`django.urls.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port.

You should **include the request as a keyword argument** to the function, for example:

Expand All @@ -44,7 +44,7 @@ You should **include the request as a keyword argument** to the function, for ex

**Signature:** `reverse_lazy(viewname, *args, **kwargs)`

Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port.
Has the same behavior as [`django.urls.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port.

As with the `reverse` function, you should **include the request as a keyword argument** to the function, for example:

Expand Down
2 changes: 1 addition & 1 deletion docs/api-guide/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ REST framework includes the following test case classes, that mirror the existin

You can use any of REST framework's test case classes as you would for the regular Django test case classes. The `self.client` attribute will be an `APIClient` instance.

from django.core.urlresolvers import reverse
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from myproject.apps.core.models import Account
Expand Down
4 changes: 2 additions & 2 deletions requirements/requirements-optionals.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Optional packages which may be used with REST framework.
markdown==2.6.4
django-guardian==1.4.3
django-filter==0.13.0
django-guardian==1.4.6
django-filter==0.14.0
coreapi==1.32.0
33 changes: 31 additions & 2 deletions rest_framework/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
from django.utils import importlib # Will be removed in Django 1.9


try:
from django.urls import (
NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
)
except ImportError:
from django.core.urlresolvers import ( # Will be removed in Django 2.0
NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
)


try:
import urlparse # Python 2.x
except ImportError:
Expand Down Expand Up @@ -128,6 +138,12 @@ def is_authenticated(user):
return user.is_authenticated


def is_anonymous(user):
if django.VERSION < (1, 10):
return user.is_anonymous()
return user.is_anonymous


def get_related_model(field):
if django.VERSION < (1, 9):
return _resolve_model(field.rel.to)
Expand Down Expand Up @@ -200,8 +216,13 @@ def value_from_object(field, obj):

if markdown.version <= '2.2':
HEADERID_EXT_PATH = 'headerid'
else:
LEVEL_PARAM = 'level'
elif markdown.version < '2.6':
HEADERID_EXT_PATH = 'markdown.extensions.headerid'
LEVEL_PARAM = 'level'
else:
HEADERID_EXT_PATH = 'markdown.extensions.toc'
LEVEL_PARAM = 'baselevel'

def apply_markdown(text):
"""
Expand All @@ -211,7 +232,7 @@ def apply_markdown(text):
extensions = [HEADERID_EXT_PATH]
extension_configs = {
HEADERID_EXT_PATH: {
'level': '2'
LEVEL_PARAM: '2'
}
}
md = markdown.Markdown(
Expand Down Expand Up @@ -277,3 +298,11 @@ def template_render(template, context=None, request=None):
# backends template, e.g. django.template.backends.django.Template
else:
return template.render(context, request=request)


def set_many(instance, field, value):
if django.VERSION < (1, 10):
setattr(instance, field, value)
else:
field = getattr(instance, field)
field.set(value)
12 changes: 12 additions & 0 deletions rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ def is_simple_callable(obj):
"""
True if the object is a callable that takes no arguments.
"""
if not hasattr(inspect, 'signature'):
return py2k_is_simple_callable(obj)

if not callable(obj):
return False

sig = inspect.signature(obj)
params = sig.parameters.values()
return all(param.default != param.empty for param in params)


def py2k_is_simple_callable(obj):
function = inspect.isfunction(obj)
method = inspect.ismethod(obj)

Expand Down
6 changes: 3 additions & 3 deletions rest_framework/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
from collections import OrderedDict

from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.core.urlresolvers import (
NoReverseMatch, Resolver404, get_script_prefix, resolve
)
from django.db.models import Manager
from django.db.models.query import QuerySet
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible, smart_text
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext_lazy as _

from rest_framework.compat import (
NoReverseMatch, Resolver404, get_script_prefix, resolve
)
from rest_framework.fields import (
Field, empty, get_attribute, is_simple_callable, iter_options
)
Expand Down
6 changes: 3 additions & 3 deletions rest_framework/reverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"""
from __future__ import unicode_literals

from django.core.urlresolvers import reverse as django_reverse
from django.core.urlresolvers import NoReverseMatch
from django.utils import six
from django.utils.functional import lazy

from rest_framework.compat import reverse as django_reverse
from rest_framework.compat import NoReverseMatch
from rest_framework.settings import api_settings
from rest_framework.utils.urls import replace_query_param

Expand Down Expand Up @@ -54,7 +54,7 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra

def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra):
"""
Same as `django.core.urlresolvers.reverse`, but optionally takes a request
Same as `django.urls.reverse`, but optionally takes a request
and returns a fully qualified URL, using the request to get the base URL.
"""
if format is not None:
Expand Down
2 changes: 1 addition & 1 deletion rest_framework/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import NoReverseMatch

from rest_framework import exceptions, renderers, views
from rest_framework.compat import NoReverseMatch
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.schemas import SchemaGenerator
Expand Down
5 changes: 3 additions & 2 deletions rest_framework/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

from django.conf import settings
from django.contrib.admindocs.views import simplify_regex
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
from django.utils import six
from django.utils.encoding import force_text

from rest_framework import exceptions, serializers
from rest_framework.compat import coreapi, uritemplate, urlparse
from rest_framework.compat import (
RegexURLPattern, RegexURLResolver, coreapi, uritemplate, urlparse
)
from rest_framework.request import clone_request
from rest_framework.views import APIView

Expand Down
10 changes: 7 additions & 3 deletions rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from django.utils.translation import ugettext_lazy as _

from rest_framework.compat import JSONField as ModelJSONField
from rest_framework.compat import postgres_fields, unicode_to_repr
from rest_framework.compat import postgres_fields, set_many, unicode_to_repr
from rest_framework.utils import model_meta
from rest_framework.utils.field_mapping import (
ClassLookupDict, get_field_kwargs, get_nested_relation_kwargs,
Expand Down Expand Up @@ -892,19 +892,23 @@ def create(self, validated_data):
# Save many-to-many relationships after the instance is created.
if many_to_many:
for field_name, value in many_to_many.items():
setattr(instance, field_name, value)
set_many(instance, field_name, value)

return instance

def update(self, instance, validated_data):
raise_errors_on_nested_writes('update', self, validated_data)
info = model_meta.get_field_info(instance)

# Simply set each attribute on the instance, and then save it.
# Note that unlike `.create()` we don't need to treat many-to-many
# relationships as being a special case. During updates we already
# have an instance pk for the relationships to be associated with.
for attr, value in validated_data.items():
setattr(instance, attr, value)
if attr in info.relations and info.relations[attr].to_many:
set_many(instance, attr, value)
else:
setattr(instance, attr, value)
instance.save()

return instance
Expand Down
3 changes: 1 addition & 2 deletions rest_framework/templatetags/rest_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import re

from django import template
from django.core.urlresolvers import NoReverseMatch, reverse
from django.template import loader
from django.utils import six
from django.utils.encoding import force_text, iri_to_uri
from django.utils.html import escape, format_html, smart_urlquote
from django.utils.safestring import SafeData, mark_safe

from rest_framework.compat import template_render
from rest_framework.compat import NoReverseMatch, reverse, template_render
from rest_framework.renderers import HTMLFormRenderer
from rest_framework.utils.urls import replace_query_param

Expand Down
2 changes: 1 addition & 1 deletion rest_framework/urlpatterns.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import unicode_literals

from django.conf.urls import include, url
from django.core.urlresolvers import RegexURLResolver

from rest_framework.compat import RegexURLResolver
from rest_framework.settings import api_settings


Expand Down
2 changes: 1 addition & 1 deletion rest_framework/utils/breadcrumbs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import unicode_literals

from django.core.urlresolvers import get_script_prefix, resolve
from rest_framework.compat import get_script_prefix, resolve


def get_breadcrumbs(url, request=None):
Expand Down
1 change: 1 addition & 0 deletions tests/browsable_api/test_form_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
class BasicSerializer(serializers.ModelSerializer):
class Meta:
model = BasicModel
fields = '__all__'


class ManyPostView(generics.GenericAPIView):
Expand Down
15 changes: 9 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
def pytest_configure():
from django.conf import settings

MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)

settings.configure(
DEBUG_PROPAGATE_EXCEPTIONS=True,
DATABASES={
Expand All @@ -21,12 +28,8 @@ def pytest_configure():
'APP_DIRS': True,
},
],
MIDDLEWARE_CLASSES=(
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
),
MIDDLEWARE=MIDDLEWARE,
MIDDLEWARE_CLASSES=MIDDLEWARE,
INSTALLED_APPS=(
'django.contrib.auth',
'django.contrib.contenttypes',
Expand Down
32 changes: 16 additions & 16 deletions tests/test_atomic_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.conf.urls import url
from django.db import connection, connections, transaction
from django.http import Http404
from django.test import TestCase, TransactionTestCase
from django.test import TestCase, TransactionTestCase, override_settings
from django.utils.decorators import method_decorator

from rest_framework import status
Expand Down Expand Up @@ -36,6 +36,20 @@ def post(self, request, *args, **kwargs):
raise APIException


class NonAtomicAPIExceptionView(APIView):
@method_decorator(transaction.non_atomic_requests)
def dispatch(self, *args, **kwargs):
return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs)

def get(self, request, *args, **kwargs):
BasicModel.objects.all()
raise Http404

urlpatterns = (
url(r'^$', NonAtomicAPIExceptionView.as_view()),
)


@unittest.skipUnless(
connection.features.uses_savepoints,
"'atomic' requires transactions and savepoints."
Expand Down Expand Up @@ -124,22 +138,8 @@ def test_api_exception_rollback_transaction(self):
connection.features.uses_savepoints,
"'atomic' requires transactions and savepoints."
)
@override_settings(ROOT_URLCONF='tests.test_atomic_requests')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, this test did not fail despite the urls property having been fully deprecated in 1.10

class NonAtomicDBTransactionAPIExceptionTests(TransactionTestCase):
@property
def urls(self):
class NonAtomicAPIExceptionView(APIView):
@method_decorator(transaction.non_atomic_requests)
def dispatch(self, *args, **kwargs):
return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs)

def get(self, request, *args, **kwargs):
BasicModel.objects.all()
raise Http404

return (
url(r'^$', NonAtomicAPIExceptionView.as_view()),
)

def setUp(self):
connections.databases['default']['ATOMIC_REQUESTS'] = True

Expand Down
3 changes: 2 additions & 1 deletion tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
)
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.compat import is_authenticated
from rest_framework.response import Response
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.views import APIView
Expand Down Expand Up @@ -408,7 +409,7 @@ class AuthAccessingRenderer(renderers.BaseRenderer):

def render(self, data, media_type=None, renderer_context=None):
request = renderer_context['request']
if request.user.is_authenticated():
if is_authenticated(request.user):
return b'authenticated'
return b'not authenticated'

Expand Down
Loading