mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-03 13:14:30 +03:00
Merge pull request #3342 from tomchristie/version-3.3
Compat updates for version 3.3
This commit is contained in:
commit
4762a73e34
11
.travis.yml
11
.travis.yml
|
@ -18,23 +18,16 @@ env:
|
|||
- TOX_ENV=py32-django16
|
||||
- TOX_ENV=py27-django16
|
||||
- TOX_ENV=py26-django16
|
||||
- TOX_ENV=py34-django15
|
||||
- TOX_ENV=py33-django15
|
||||
- TOX_ENV=py32-django15
|
||||
- TOX_ENV=py27-django15
|
||||
- TOX_ENV=py26-django15
|
||||
- TOX_ENV=py27-djangomaster
|
||||
- TOX_ENV=py32-djangomaster
|
||||
- TOX_ENV=py33-djangomaster
|
||||
- TOX_ENV=py34-djangomaster
|
||||
- TOX_ENV=py35-djangomaster
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: TOX_ENV=py27-djangomaster
|
||||
- env: TOX_ENV=py32-djangomaster
|
||||
- env: TOX_ENV=py33-djangomaster
|
||||
- env: TOX_ENV=py34-djangomaster
|
||||
- env: TOX_ENV=py35-djangomaster
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
|
|
@ -37,7 +37,7 @@ There is a live example API for testing purposes, [available here][sandbox].
|
|||
# Requirements
|
||||
|
||||
* Python (2.6.5+, 2.7, 3.2, 3.3, 3.4)
|
||||
* Django (1.5.6+, 1.6.3+, 1.7, 1.8)
|
||||
* Django (1.6.3+, 1.7, 1.8)
|
||||
|
||||
# Installation
|
||||
|
||||
|
|
|
@ -438,6 +438,7 @@ Declaring a `ModelSerializer` looks like this:
|
|||
class AccountSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = ('id', 'account_name', 'users', 'created')
|
||||
|
||||
By default, all the model fields on the class will be mapped to a corresponding serializer fields.
|
||||
|
||||
|
@ -459,7 +460,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
|
|||
|
||||
## Specifying which fields to include
|
||||
|
||||
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`.
|
||||
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. It is strongly recommended that you explicitly set all fields that should be serialized using the `fields` attribute. This will make it less likely to result in unintentionally exposing data when your models change.
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -468,7 +469,27 @@ For example:
|
|||
model = Account
|
||||
fields = ('id', 'account_name', 'users', 'created')
|
||||
|
||||
The names in the `fields` option will normally map to model fields on the model class.
|
||||
You can also set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used.
|
||||
|
||||
For example:
|
||||
|
||||
class AccountSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = '__all__'
|
||||
|
||||
You can set the `exclude` attribute of the to a list of fields to be excluded from the serializer.
|
||||
|
||||
For example:
|
||||
|
||||
class AccountSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Account
|
||||
exclude = ('users',)
|
||||
|
||||
In the example above, if the `Account` model had 3 fields `account_name`, `users`, and `created`, this will result in the fields `account_name` and `created` to be serialized.
|
||||
|
||||
The names in the `fields` and `exclude` attributes will normally map to model fields on the model class.
|
||||
|
||||
Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class.
|
||||
|
||||
|
|
|
@ -38,6 +38,14 @@ You can determine your currently installed version using `pip freeze`:
|
|||
|
||||
---
|
||||
|
||||
## 3.3.x series
|
||||
|
||||
### 3.3.0
|
||||
|
||||
**Date**: NOT YET RELEASED
|
||||
|
||||
* Removed support for Django Version 1.5 ([#3421][gh3421])
|
||||
|
||||
## 3.2.x series
|
||||
|
||||
### 3.2.4
|
||||
|
@ -533,3 +541,6 @@ For older release notes, [please see the version 2.x documentation][old-release-
|
|||
[gh3361]: https://github.com/tomchristie/django-rest-framework/issues/3361
|
||||
[gh3364]: https://github.com/tomchristie/django-rest-framework/issues/3364
|
||||
[gh3415]: https://github.com/tomchristie/django-rest-framework/issues/3415
|
||||
|
||||
<!-- 3.3.0 -->
|
||||
[gh3421]: https://github.com/tomchristie/django-rest-framework/pulls/3421
|
||||
|
|
|
@ -67,6 +67,14 @@ except ImportError:
|
|||
from django.utils.datastructures import SortedDict as OrderedDict
|
||||
|
||||
|
||||
# unittest.SkipUnless only available in Python 2.7.
|
||||
try:
|
||||
import unittest
|
||||
unittest.skipUnless
|
||||
except (ImportError, AttributeError):
|
||||
from django.utils import unittest
|
||||
|
||||
|
||||
# contrib.postgres only supported from 1.8 onwards.
|
||||
try:
|
||||
from django.contrib.postgres import fields as postgres_fields
|
||||
|
@ -74,23 +82,30 @@ except ImportError:
|
|||
postgres_fields = None
|
||||
|
||||
|
||||
# Apps only exists from 1.7 onwards.
|
||||
try:
|
||||
from django.apps import apps
|
||||
get_model = apps.get_model
|
||||
except ImportError:
|
||||
from django.db.models import get_model
|
||||
|
||||
|
||||
# Import path changes from 1.7 onwards.
|
||||
try:
|
||||
from django.contrib.contenttypes.fields import (
|
||||
GenericForeignKey, GenericRelation
|
||||
)
|
||||
except ImportError:
|
||||
from django.contrib.contenttypes.generic import (
|
||||
GenericForeignKey, GenericRelation
|
||||
)
|
||||
|
||||
# django-filter is optional
|
||||
try:
|
||||
import django_filters
|
||||
except ImportError:
|
||||
django_filters = None
|
||||
|
||||
if django.VERSION >= (1, 6):
|
||||
def clean_manytomany_helptext(text):
|
||||
return text
|
||||
else:
|
||||
# Up to version 1.5 many to many fields automatically suffix
|
||||
# the `help_text` attribute with hardcoded text.
|
||||
def clean_manytomany_helptext(text):
|
||||
if text.endswith(' Hold down "Control", or "Command" on a Mac, to select more than one.'):
|
||||
text = text[:-69]
|
||||
return text
|
||||
|
||||
# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
|
||||
# Fixes (#1712). We keep the try/except for the test suite.
|
||||
guardian = None
|
||||
|
@ -101,14 +116,6 @@ except ImportError:
|
|||
pass
|
||||
|
||||
|
||||
def get_model_name(model_cls):
|
||||
try:
|
||||
return model_cls._meta.model_name
|
||||
except AttributeError:
|
||||
# < 1.6 used module_name instead of model_name
|
||||
return model_cls._meta.module_name
|
||||
|
||||
|
||||
# MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+
|
||||
if django.VERSION >= (1, 8):
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
@ -144,32 +151,6 @@ else:
|
|||
super(MaxLengthValidator, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
# URLValidator only accepts `message` in 1.6+
|
||||
if django.VERSION >= (1, 6):
|
||||
from django.core.validators import URLValidator
|
||||
else:
|
||||
from django.core.validators import URLValidator as DjangoURLValidator
|
||||
|
||||
|
||||
class URLValidator(DjangoURLValidator):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.message = kwargs.pop('message', self.message)
|
||||
super(URLValidator, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
# EmailValidator requires explicit regex prior to 1.6+
|
||||
if django.VERSION >= (1, 6):
|
||||
from django.core.validators import EmailValidator
|
||||
else:
|
||||
from django.core.validators import EmailValidator as DjangoEmailValidator
|
||||
from django.core.validators import email_re
|
||||
|
||||
|
||||
class EmailValidator(DjangoEmailValidator):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EmailValidator, self).__init__(email_re, *args, **kwargs)
|
||||
|
||||
|
||||
# PATCH method is not implemented by Django
|
||||
if 'patch' not in View.http_method_names:
|
||||
View.http_method_names = View.http_method_names + ['patch']
|
||||
|
|
|
@ -11,7 +11,9 @@ import uuid
|
|||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.validators import RegexValidator, ip_address_validators
|
||||
from django.core.validators import (
|
||||
EmailValidator, RegexValidator, URLValidator, ip_address_validators
|
||||
)
|
||||
from django.forms import FilePathField as DjangoFilePathField
|
||||
from django.forms import ImageField as DjangoImageField
|
||||
from django.utils import six, timezone
|
||||
|
@ -23,9 +25,9 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from rest_framework import ISO_8601
|
||||
from rest_framework.compat import (
|
||||
EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
||||
MinValueValidator, OrderedDict, URLValidator, duration_string,
|
||||
parse_duration, unicode_repr, unicode_to_repr
|
||||
MaxLengthValidator, MaxValueValidator, MinLengthValidator,
|
||||
MinValueValidator, OrderedDict, duration_string, parse_duration,
|
||||
unicode_repr, unicode_to_repr
|
||||
)
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.settings import api_settings
|
||||
|
|
|
@ -11,9 +11,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
from django.db import models
|
||||
from django.utils import six
|
||||
|
||||
from rest_framework.compat import (
|
||||
distinct, django_filters, get_model_name, guardian
|
||||
)
|
||||
from rest_framework.compat import distinct, django_filters, guardian
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
FilterSet = django_filters and django_filters.FilterSet or None
|
||||
|
@ -202,7 +200,7 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend):
|
|||
model_cls = queryset.model
|
||||
kwargs = {
|
||||
'app_label': model_cls._meta.app_label,
|
||||
'model_name': get_model_name(model_cls)
|
||||
'model_name': model_cls._meta.model_name
|
||||
}
|
||||
permission = self.perm_format % kwargs
|
||||
if guardian.VERSION >= (1, 3):
|
||||
|
|
|
@ -5,8 +5,6 @@ from __future__ import unicode_literals
|
|||
|
||||
from django.http import Http404
|
||||
|
||||
from rest_framework.compat import get_model_name
|
||||
|
||||
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
|
||||
|
||||
|
||||
|
@ -104,7 +102,7 @@ class DjangoModelPermissions(BasePermission):
|
|||
"""
|
||||
kwargs = {
|
||||
'app_label': model_cls._meta.app_label,
|
||||
'model_name': get_model_name(model_cls)
|
||||
'model_name': model_cls._meta.model_name
|
||||
}
|
||||
return [perm % kwargs for perm in self.perms_map[method]]
|
||||
|
||||
|
@ -166,7 +164,7 @@ class DjangoObjectPermissions(DjangoModelPermissions):
|
|||
def get_required_object_permissions(self, method, model_cls):
|
||||
kwargs = {
|
||||
'app_label': model_cls._meta.app_label,
|
||||
'model_name': get_model_name(model_cls)
|
||||
'model_name': model_cls._meta.model_name
|
||||
}
|
||||
return [perm % kwargs for perm in self.perms_map[method]]
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ class Response(SimpleTemplateResponse):
|
|||
state = super(Response, self).__getstate__()
|
||||
for key in (
|
||||
'accepted_renderer', 'renderer_context', 'resolver_match',
|
||||
'client', 'request', 'wsgi_request'
|
||||
'client', 'request', 'json', 'wsgi_request'
|
||||
):
|
||||
if key in state:
|
||||
del state[key]
|
||||
|
|
|
@ -12,6 +12,8 @@ response content is handled by parsers and renderers.
|
|||
"""
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import warnings
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.fields import Field as DjangoModelField
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
|
@ -51,6 +53,8 @@ LIST_SERIALIZER_KWARGS = (
|
|||
'instance', 'data', 'partial', 'context', 'allow_null'
|
||||
)
|
||||
|
||||
ALL_FIELDS = '__all__'
|
||||
|
||||
|
||||
# BaseSerializer
|
||||
# --------------
|
||||
|
@ -957,10 +961,10 @@ class ModelSerializer(Serializer):
|
|||
fields = getattr(self.Meta, 'fields', None)
|
||||
exclude = getattr(self.Meta, 'exclude', None)
|
||||
|
||||
if fields and not isinstance(fields, (list, tuple)):
|
||||
if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)):
|
||||
raise TypeError(
|
||||
'The `fields` option must be a list or tuple. Got %s.' %
|
||||
type(fields).__name__
|
||||
'The `fields` option must be a list or tuple or "__all__". '
|
||||
'Got %s.' % type(fields).__name__
|
||||
)
|
||||
|
||||
if exclude and not isinstance(exclude, (list, tuple)):
|
||||
|
@ -976,6 +980,20 @@ class ModelSerializer(Serializer):
|
|||
)
|
||||
)
|
||||
|
||||
if fields is None and exclude is None:
|
||||
warnings.warn(
|
||||
"Creating a ModelSerializer without either the 'fields' "
|
||||
"attribute or the 'exclude' attribute is pending deprecation "
|
||||
"since 3.3.0. Add an explicit fields = '__all__' to the "
|
||||
"{serializer_class} serializer.".format(
|
||||
serializer_class=self.__class__.__name__
|
||||
),
|
||||
PendingDeprecationWarning
|
||||
)
|
||||
|
||||
if fields == ALL_FIELDS:
|
||||
fields = None
|
||||
|
||||
if fields is not None:
|
||||
# Ensure that all declared fields have also been included in the
|
||||
# `Meta.fields` option.
|
||||
|
|
|
@ -41,8 +41,9 @@ def optional_login(request):
|
|||
except NoReverseMatch:
|
||||
return ''
|
||||
|
||||
snippet = "<li><a href='{href}?next={next}'>Log in</a></li>".format(href=login_url, next=escape(request.path))
|
||||
return snippet
|
||||
snippet = "<li><a href='{href}?next={next}'>Log in</a></li>"
|
||||
snippet = snippet.format(href=login_url, next=escape(request.path))
|
||||
return mark_safe(snippet)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
|
@ -64,8 +65,8 @@ def optional_logout(request, user):
|
|||
<li><a href='{href}?next={next}'>Log out</a></li>
|
||||
</ul>
|
||||
</li>"""
|
||||
|
||||
return snippet.format(user=user, href=logout_url, next=escape(request.path))
|
||||
snippet = snippet.format(user=escape(user), href=logout_url, next=escape(request.path))
|
||||
return mark_safe(snippet)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
|
|
|
@ -8,7 +8,6 @@ from django.core import validators
|
|||
from django.db import models
|
||||
from django.utils.text import capfirst
|
||||
|
||||
from rest_framework.compat import clean_manytomany_helptext
|
||||
from rest_framework.validators import UniqueValidator
|
||||
|
||||
NUMERIC_FIELD_TYPES = (
|
||||
|
@ -230,7 +229,7 @@ def get_relation_kwargs(field_name, relation_info):
|
|||
if model_field:
|
||||
if model_field.verbose_name and needs_label(model_field, field_name):
|
||||
kwargs['label'] = capfirst(model_field.verbose_name)
|
||||
help_text = clean_manytomany_helptext(model_field.help_text)
|
||||
help_text = model_field.help_text
|
||||
if help_text:
|
||||
kwargs['help_text'] = help_text
|
||||
if not model_field.editable:
|
||||
|
|
|
@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
from django.db import models
|
||||
from django.utils import six
|
||||
|
||||
from rest_framework.compat import OrderedDict
|
||||
from rest_framework.compat import OrderedDict, get_model
|
||||
|
||||
FieldInfo = namedtuple('FieldResult', [
|
||||
'pk', # Model field instance
|
||||
|
@ -45,7 +45,7 @@ def _resolve_model(obj):
|
|||
"""
|
||||
if isinstance(obj, six.string_types) and len(obj.split('.')) == 2:
|
||||
app_name, model_name = obj.split('.')
|
||||
resolved_model = models.get_model(app_name, model_name)
|
||||
resolved_model = get_model(app_name, model_name)
|
||||
if resolved_model is None:
|
||||
msg = "Django did not return a model for {0}.{1}"
|
||||
raise ImproperlyConfigured(msg.format(app_name, model_name))
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
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.utils.decorators import method_decorator
|
||||
from django.utils.unittest import skipUnless
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.compat import unittest
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.test import APIRequestFactory
|
||||
|
@ -35,8 +35,10 @@ class APIExceptionView(APIView):
|
|||
raise APIException
|
||||
|
||||
|
||||
@skipUnless(connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints.")
|
||||
@unittest.skipUnless(
|
||||
connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints."
|
||||
)
|
||||
class DBTransactionTests(TestCase):
|
||||
def setUp(self):
|
||||
self.view = BasicView.as_view()
|
||||
|
@ -55,8 +57,10 @@ class DBTransactionTests(TestCase):
|
|||
assert BasicModel.objects.count() == 1
|
||||
|
||||
|
||||
@skipUnless(connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints.")
|
||||
@unittest.skipUnless(
|
||||
connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints."
|
||||
)
|
||||
class DBTransactionErrorTests(TestCase):
|
||||
def setUp(self):
|
||||
self.view = ErrorView.as_view()
|
||||
|
@ -83,8 +87,10 @@ class DBTransactionErrorTests(TestCase):
|
|||
assert BasicModel.objects.count() == 1
|
||||
|
||||
|
||||
@skipUnless(connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints.")
|
||||
@unittest.skipUnless(
|
||||
connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints."
|
||||
)
|
||||
class DBTransactionAPIExceptionTests(TestCase):
|
||||
def setUp(self):
|
||||
self.view = APIExceptionView.as_view()
|
||||
|
@ -113,8 +119,10 @@ class DBTransactionAPIExceptionTests(TestCase):
|
|||
assert BasicModel.objects.count() == 0
|
||||
|
||||
|
||||
@skipUnless(connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints.")
|
||||
@unittest.skipUnless(
|
||||
connection.features.uses_savepoints,
|
||||
"'atomic' requires transactions and savepoints."
|
||||
)
|
||||
class NonAtomicDBTransactionAPIExceptionTests(TransactionTestCase):
|
||||
@property
|
||||
def urls(self):
|
||||
|
@ -127,9 +135,8 @@ class NonAtomicDBTransactionAPIExceptionTests(TransactionTestCase):
|
|||
BasicModel.objects.all()
|
||||
raise Http404
|
||||
|
||||
return patterns(
|
||||
'',
|
||||
url(r'^$', NonAtomicAPIExceptionView.as_view())
|
||||
return (
|
||||
url(r'^$', NonAtomicAPIExceptionView.as_view()),
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -8,12 +8,11 @@ from django.core.urlresolvers import reverse
|
|||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import unittest
|
||||
from django.utils.dateparse import parse_date
|
||||
from django.utils.six.moves import reload_module
|
||||
|
||||
from rest_framework import filters, generics, serializers, status
|
||||
from rest_framework.compat import django_filters
|
||||
from rest_framework.compat import django_filters, unittest
|
||||
from rest_framework.test import APIRequestFactory
|
||||
|
||||
from .models import BaseFilterableItem, BasicModel, FilterableItem
|
||||
|
|
|
@ -321,6 +321,21 @@ class TestRegularFieldMappings(TestCase):
|
|||
|
||||
ExampleSerializer()
|
||||
|
||||
def test_fields_and_exclude_behavior(self):
|
||||
class ImplicitFieldsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RegularFieldsModel
|
||||
|
||||
class ExplicitFieldsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RegularFieldsModel
|
||||
fields = '__all__'
|
||||
|
||||
implicit = ImplicitFieldsSerializer()
|
||||
explicit = ExplicitFieldsSerializer()
|
||||
|
||||
assert implicit.data == explicit.data
|
||||
|
||||
|
||||
@pytest.mark.skipif(django.VERSION < (1, 8),
|
||||
reason='DurationField is only available for django1.8+')
|
||||
|
|
|
@ -6,13 +6,12 @@ from django.contrib.auth.models import Group, Permission, User
|
|||
from django.core.urlresolvers import ResolverMatch
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.utils import unittest
|
||||
|
||||
from rest_framework import (
|
||||
HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers,
|
||||
status
|
||||
)
|
||||
from rest_framework.compat import get_model_name, guardian
|
||||
from rest_framework.compat import guardian, unittest
|
||||
from rest_framework.filters import DjangoObjectPermissionsFilter
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from rest_framework.test import APIRequestFactory
|
||||
|
@ -279,7 +278,7 @@ class ObjectPermissionsIntegrationTests(TestCase):
|
|||
|
||||
# give everyone model level permissions, as we are not testing those
|
||||
everyone = Group.objects.create(name='everyone')
|
||||
model_name = get_model_name(BasicPermModel)
|
||||
model_name = BasicPermModel._meta.model_name
|
||||
app_label = BasicPermModel._meta.app_label
|
||||
f = '{0}_{1}'.format
|
||||
perms = {
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.contenttypes.generic import (
|
||||
GenericForeignKey, GenericRelation
|
||||
)
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.compat import GenericForeignKey, GenericRelation
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
|
|
|
@ -150,12 +150,12 @@ class ResolveModelWithPatchedDjangoTests(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""Monkeypatch get_model."""
|
||||
self.get_model = rest_framework.utils.model_meta.models.get_model
|
||||
self.get_model = rest_framework.utils.model_meta.get_model
|
||||
|
||||
def get_model(app_label, model_name):
|
||||
return None
|
||||
|
||||
rest_framework.utils.model_meta.models.get_model = get_model
|
||||
rest_framework.utils.model_meta.get_model = get_model
|
||||
|
||||
def tearDown(self):
|
||||
"""Revert monkeypatching."""
|
||||
|
|
36
tox.ini
36
tox.ini
|
@ -4,15 +4,23 @@ addopts=--tb=short
|
|||
[tox]
|
||||
envlist =
|
||||
py27-{lint,docs},
|
||||
{py26,py27,py32,py33,py34}-django{15,16},
|
||||
{py27,py32,py33,py34}-django{17,18,master}
|
||||
{py26,py27,py32,py33,py34}-django16,
|
||||
{py27,py32,py33,py34}-django{17,18},
|
||||
{py27,py34,py35}-django{master}
|
||||
|
||||
[testenv]
|
||||
basepython =
|
||||
py26: python2.6
|
||||
py27: python2.7
|
||||
py32: python3.2
|
||||
py33: python3.3
|
||||
py34: python3.4
|
||||
py35: python3.5
|
||||
|
||||
commands = ./runtests.py --fast {posargs} --coverage
|
||||
setenv =
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
deps =
|
||||
django15: Django==1.5.6 # Should track minimum supported
|
||||
django16: Django==1.6.3 # Should track minimum supported
|
||||
django17: Django==1.7.10 # Should track maximum supported
|
||||
django18: Django==1.8.4 # Should track maximum supported
|
||||
|
@ -31,3 +39,25 @@ commands = mkdocs build
|
|||
deps =
|
||||
-rrequirements/requirements-testing.txt
|
||||
-rrequirements/requirements-documentation.txt
|
||||
|
||||
# Specify explicitly to exclude Django Guardian against Django master (various Pythons)
|
||||
[testenv:py27-djangomaster]
|
||||
deps =
|
||||
https://github.com/django/django/archive/master.tar.gz
|
||||
-rrequirements/requirements-testing.txt
|
||||
markdown==2.5.2
|
||||
django-filter==0.10.0
|
||||
[testenv:py34-djangomaster]
|
||||
deps =
|
||||
https://github.com/django/django/archive/master.tar.gz
|
||||
-rrequirements/requirements-testing.txt
|
||||
markdown==2.5.2
|
||||
django-filter==0.10.0
|
||||
|
||||
[testenv:py35-djangomaster]
|
||||
deps =
|
||||
https://github.com/django/django/archive/master.tar.gz
|
||||
-rrequirements/requirements-testing.txt
|
||||
markdown==2.5.2
|
||||
django-filter==0.10.0
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user