diff --git a/.travis.yml b/.travis.yml index d68c4c679..2bd24b566 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ sudo: false language: python python: - - "2.7" - "3.4" - "3.5" - "3.6" diff --git a/README.rst b/README.rst index 772babe44..251470fcc 100644 --- a/README.rst +++ b/README.rst @@ -19,8 +19,8 @@ Full documentation on `read the docs`_. Requirements ------------ -* **Python**: 2.7, 3.4, 3.5, 3.6 -* **Django**: 1.8, 1.10, 1.11 +* **Python**: 3.4, 3.5, 3.6 +* **Django**: 1.11, 2.0b * **DRF**: 3.7 Installation diff --git a/django_filters/__init__.py b/django_filters/__init__.py index 77f911904..d552d509a 100644 --- a/django_filters/__init__.py +++ b/django_filters/__init__.py @@ -1,6 +1,4 @@ # flake8: noqa -from __future__ import absolute_import - import pkgutil from .constants import STRICTNESS diff --git a/django_filters/compat.py b/django_filters/compat.py index 69945c404..fcaa9e9b4 100644 --- a/django_filters/compat.py +++ b/django_filters/compat.py @@ -1,14 +1,4 @@ - -from __future__ import absolute_import - -import django from django.conf import settings -from django.utils.timezone import make_aware as make_aware_orig - -try: - from django.forms.utils import pretty_name -except ImportError: # Django 1.8 - from django.forms.forms import pretty_name # django-crispy-forms is optional try: @@ -33,39 +23,3 @@ def is_crispy(): import coreschema except ImportError: coreschema = None - -def remote_field(field): - """ - https://docs.djangoproject.com/en/1.9/releases/1.9/#field-rel-changes - """ - if django.VERSION >= (1, 9): - return field.remote_field - return field.rel - - -def remote_model(field): - if django.VERSION >= (1, 9): - return remote_field(field).model - return remote_field(field).to - - -def remote_queryset(field): - model = remote_model(field) - limit_choices_to = field.get_limit_choices_to() - - return model._default_manager.complex_filter(limit_choices_to) - - -def format_value(widget, value): - if django.VERSION >= (1, 10): - return widget.format_value(value) - return widget._format_value(value) - - - -def make_aware(value, timezone, is_dst): - """is_dst was added for 1.9""" - if django.VERSION >= (1, 9): - return make_aware_orig(value, timezone, is_dst) - else: - return make_aware_orig(value, timezone) diff --git a/django_filters/conf.py b/django_filters/conf.py index b0a102767..cf8a4599f 100644 --- a/django_filters/conf.py +++ b/django_filters/conf.py @@ -1,6 +1,3 @@ - -from __future__ import absolute_import - from django.conf import settings as dj_settings from django.core.signals import setting_changed from django.utils.translation import ugettext_lazy as _ diff --git a/django_filters/fields.py b/django_filters/fields.py index 99a107651..3150fa469 100644 --- a/django_filters/fields.py +++ b/django_filters/fields.py @@ -1,9 +1,6 @@ -from __future__ import absolute_import, unicode_literals - from collections import namedtuple from datetime import datetime, time -import django from django import forms from django.utils.dateparse import parse_datetime from django.utils.encoding import force_str @@ -237,14 +234,7 @@ def __init__(self, *args, **kwargs): super(ChoiceIteratorMixin, self).__init__(*args, **kwargs) def _get_choices(self): - if django.VERSION >= (1, 11): - return super(ChoiceIteratorMixin, self)._get_choices() - - # HACK: Django < 1.11 does not allow a custom iterator to be provided. - # This code only executes for Model*ChoiceFields. - if hasattr(self, '_choices'): - return self._choices - return self.iterator(self) + return super(ChoiceIteratorMixin, self)._get_choices() def _set_choices(self, value): super(ChoiceIteratorMixin, self)._set_choices(value) diff --git a/django_filters/filters.py b/django_filters/filters.py index 90701a27d..e64fd100a 100644 --- a/django_filters/filters.py +++ b/django_filters/filters.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from collections import OrderedDict from datetime import timedelta @@ -7,12 +5,11 @@ from django.db.models import Q from django.db.models.constants import LOOKUP_SEP from django.db.models.sql.constants import QUERY_TERMS -from django.utils import six +from django.forms.utils import pretty_name from django.utils.itercompat import is_iterable from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ -from .compat import pretty_name from .conf import settings from .constants import EMPTY_VALUES from .fields import ( @@ -186,7 +183,7 @@ def field(self): def filter(self, qs, value): if isinstance(value, Lookup): - lookup = six.text_type(value.lookup_type) + lookup = str(value.lookup_type) value = value.value else: lookup = self.lookup_expr @@ -475,7 +472,7 @@ class DateRangeFilter(ChoiceFilter): def __init__(self, *args, **kwargs): kwargs['choices'] = [ - (key, value[0]) for key, value in six.iteritems(self.options)] + (key, value[0]) for key, value in self.options.items()] # empty/null choices not relevant kwargs.setdefault('empty_label', None) @@ -660,13 +657,13 @@ def normalize_fields(cls, fields): "'fields' must be an iterable (e.g., a list, tuple, or mapping)." # fields is an iterable of field names - assert all(isinstance(field, six.string_types) or + assert all(isinstance(field, str) or is_iterable(field) and len(field) == 2 # may need to be wrapped in parens for field in fields), \ "'fields' must contain strings or (field name, param name) pairs." return OrderedDict([ - (f, f) if isinstance(f, six.string_types) else f for f in fields + (f, f) if isinstance(f, str) else f for f in fields ]) def build_choices(self, fields, labels): diff --git a/django_filters/filterset.py b/django_filters/filterset.py index da100952e..a33540ff3 100644 --- a/django_filters/filterset.py +++ b/django_filters/filterset.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import copy from collections import OrderedDict @@ -7,9 +5,7 @@ from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.fields.related import ForeignObjectRel -from django.utils import six -from .compat import remote_field, remote_queryset from .conf import settings from .constants import ALL_FIELDS, STRICTNESS from .filters import ( @@ -36,6 +32,13 @@ ) +def remote_queryset(field): + model = field.remote_field.model + limit_choices_to = field.get_limit_choices_to() + + return model._default_manager.complex_filter(limit_choices_to) + + class FilterSetOptions(object): def __init__(self, options=None): self.model = getattr(options, 'model', None) @@ -113,7 +116,7 @@ def get_declared_filters(cls, bases, attrs): 'filter_class': ModelChoiceFilter, 'extra': lambda f: { 'queryset': remote_queryset(f), - 'to_field_name': remote_field(f).field_name, + 'to_field_name': f.remote_field.field_name, 'null_label': settings.NULL_CHOICE_LABEL if f.null else None, } }, @@ -121,7 +124,7 @@ def get_declared_filters(cls, bases, attrs): 'filter_class': ModelChoiceFilter, 'extra': lambda f: { 'queryset': remote_queryset(f), - 'to_field_name': remote_field(f).field_name, + 'to_field_name': f.remote_field.field_name, 'null_label': settings.NULL_CHOICE_LABEL if f.null else None, } }, @@ -181,7 +184,7 @@ def qs(self): # start with all the results and filter from there qs = self.queryset.all() - for name, filter_ in six.iteritems(self.filters): + for name, filter_ in self.filters.items(): value = self.form.cleaned_data.get(name) if value is not None: # valid & clean data @@ -196,7 +199,7 @@ def form(self): if not hasattr(self, '_form'): fields = OrderedDict([ (name, filter_.field) - for name, filter_ in six.iteritems(self.filters)]) + for name, filter_ in self.filters.items()]) Form = type(str('%sForm' % self.__class__.__name__), (self._meta.form,), fields) @@ -328,7 +331,7 @@ def filter_for_field(cls, f, field_name, lookup_expr='exact'): @classmethod def filter_for_reverse_field(cls, f, field_name): - rel = remote_field(f.field) + rel = f.field.remote_field queryset = f.field.model._default_manager.all() default = { 'field_name': field_name, @@ -410,7 +413,7 @@ def _csv_filter_class_name(cls, filter_class, lookup_type): return str('%s%sFilter' % (type_name, lookup_name)) -class FilterSet(six.with_metaclass(FilterSetMetaclass, BaseFilterSet)): +class FilterSet(BaseFilterSet, metaclass=FilterSetMetaclass): pass diff --git a/django_filters/rest_framework/__init__.py b/django_filters/rest_framework/__init__.py index 11af6d59e..55025e705 100644 --- a/django_filters/rest_framework/__init__.py +++ b/django_filters/rest_framework/__init__.py @@ -1,5 +1,4 @@ # flake8: noqa -from __future__ import absolute_import from .backends import DjangoFilterBackend from .filterset import FilterSet from .filters import * diff --git a/django_filters/rest_framework/backends.py b/django_filters/rest_framework/backends.py index a837324cf..eeba17361 100644 --- a/django_filters/rest_framework/backends.py +++ b/django_filters/rest_framework/backends.py @@ -1,10 +1,6 @@ - -from __future__ import absolute_import - import warnings from django.template import loader -from django.utils import six from . import filters, filterset from .. import compat @@ -74,7 +70,7 @@ def get_coreschema_field(self, field): else: field_cls = compat.coreschema.String return field_cls( - description=six.text_type(field.extra.get('help_text', '')) + description=str(field.extra.get('help_text', '')) ) def get_schema_fields(self, view): diff --git a/django_filters/rest_framework/filterset.py b/django_filters/rest_framework/filterset.py index 9a984f25f..48d2d66c3 100644 --- a/django_filters/rest_framework/filterset.py +++ b/django_filters/rest_framework/filterset.py @@ -1,6 +1,3 @@ - -from __future__ import absolute_import - from copy import deepcopy from django import forms diff --git a/django_filters/utils.py b/django_filters/utils.py index 1531fadb9..f94c3aa50 100644 --- a/django_filters/utils.py +++ b/django_filters/utils.py @@ -9,12 +9,11 @@ from django.db.models.fields import FieldDoesNotExist from django.db.models.fields.related import ForeignObjectRel, RelatedField from django.forms import ValidationError -from django.utils import six, timezone +from django.utils import timezone from django.utils.encoding import force_text from django.utils.text import capfirst from django.utils.translation import ugettext as _ -from .compat import make_aware, remote_field, remote_model from .exceptions import FieldLookupError @@ -50,7 +49,7 @@ def get_all_model_fields(model): return [ f.name for f in sorted(opts.fields + opts.many_to_many) if not isinstance(f, models.AutoField) and - not (getattr(remote_field(f), 'parent_link', False)) + not (getattr(f.remote_field, 'parent_link', False)) ] @@ -93,7 +92,7 @@ def get_field_parts(model, field_name): fields.append(field) if isinstance(field, RelatedField): - opts = remote_model(field)._meta + opts = field.remote_field.model._meta elif isinstance(field, ForeignObjectRel): opts = field.related_model._meta @@ -141,12 +140,12 @@ def resolve_field(model_field, lookup_expr): lhs = query.try_transform(*args) lookups = lookups[1:] except FieldError as e: - six.raise_from(FieldLookupError(model_field, lookup_expr), e) + raise FieldLookupError(model_field, lookup_expr) from e def handle_timezone(value, is_dst=None): if settings.USE_TZ and timezone.is_naive(value): - return make_aware(value, timezone.get_current_timezone(), is_dst) + return timezone.make_aware(value, timezone.get_current_timezone(), is_dst) elif not settings.USE_TZ and timezone.is_aware(value): return timezone.make_naive(value, timezone.utc) return value @@ -225,7 +224,7 @@ def label_for_filter(model, field_name, lookup_expr, exclude=False): verbose_expression = [_('exclude'), name] if exclude else [name] # iterable lookups indicate a LookupTypeField, which should not be verbose - if isinstance(lookup_expr, six.string_types): + if isinstance(lookup_expr, str): verbose_expression += [verbose_lookup_expr(lookup_expr)] verbose_expression = [force_text(part) for part in verbose_expression if part] diff --git a/django_filters/views.py b/django_filters/views.py index 8000548f7..2303ed9df 100644 --- a/django_filters/views.py +++ b/django_filters/views.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from django.core.exceptions import ImproperlyConfigured from django.views.generic import View from django.views.generic.list import ( diff --git a/django_filters/widgets.py b/django_filters/widgets.py index 572ee8507..ba1d2c16c 100644 --- a/django_filters/widgets.py +++ b/django_filters/widgets.py @@ -1,10 +1,7 @@ -from __future__ import absolute_import, unicode_literals - from collections import Iterable from itertools import chain from re import search, sub -import django from django import forms from django.db.models.fields import BLANK_CHOICE_DASH from django.forms.utils import flatatt @@ -12,11 +9,8 @@ from django.utils.encoding import force_text from django.utils.http import urlencode from django.utils.safestring import mark_safe -from django.utils.six import string_types from django.utils.translation import ugettext as _ -from .compat import format_value - class LinkWidget(forms.Widget): def __init__(self, attrs=None, choices=()): @@ -34,10 +28,7 @@ def render(self, name, value, attrs=None, choices=()): self.data = {} if value is None: value = '' - if django.VERSION < (1, 11): - final_attrs = self.build_attrs(attrs) - else: - final_attrs = self.build_attrs(self.attrs, extra_attrs=attrs) + final_attrs = self.build_attrs(self.attrs, extra_attrs=attrs) output = ['' % flatatt(final_attrs)] options = self.render_options(choices, [value], name) if options: @@ -118,14 +109,6 @@ def value_omitted_from_data(self, data, files, name): for widget, suffix in zip(self.widgets, self.suffixes) ) - # Django < 1.11 compat - def format_output(self, rendered_widgets): - rendered_widgets = [ - self.replace_name(output, i) - for i, output in enumerate(rendered_widgets) - ] - return '\n'.join(rendered_widgets) - def replace_name(self, output, index): result = search(r'name="(?P.*)_%d"' % index, output) name = result.group('name') @@ -147,10 +130,6 @@ def __init__(self, attrs=None): widgets = (forms.TextInput, forms.TextInput) super(RangeWidget, self).__init__(widgets, attrs) - def format_output(self, rendered_widgets): - # Method was removed in Django 1.11. - return '-'.join(rendered_widgets) - def decompress(self, value): if value: return [value.start, value.stop] @@ -189,7 +168,7 @@ def render(self, name, value, attrs=None): def value_from_datadict(self, data, files, name): value = data.get(name, None) - if isinstance(value, string_types): + if isinstance(value, str): value = value.lower() return { @@ -204,7 +183,7 @@ def value_from_datadict(self, data, files, name): class BaseCSVWidget(forms.Widget): def _isiterable(self, value): - return isinstance(value, Iterable) and not isinstance(value, string_types) + return isinstance(value, Iterable) and not isinstance(value, str) def value_from_datadict(self, data, files, name): value = super(BaseCSVWidget, self).value_from_datadict(data, files, name) @@ -227,7 +206,7 @@ def render(self, name, value, attrs=None): # if we have multiple values, we need to force render as a text input # (otherwise, the additional values are lost) surrogate = forms.TextInput() - value = [force_text(format_value(surrogate, v)) for v in value] + value = [force_text(surrogate.format_value(v)) for v in value] value = ','.join(list(value)) return surrogate.render(name, value, attrs) @@ -252,7 +231,7 @@ def value_from_datadict(self, data, files, name): if not isinstance(data, MultiValueDict): for key, value in data.items(): # treat value as csv string: ?foo=1,2 - if isinstance(value, string_types): + if isinstance(value, str): data[key] = [x.strip() for x in value.rstrip(',').split(',') if x] data = MultiValueDict(data) diff --git a/docs/guide/install.txt b/docs/guide/install.txt index 8572ab881..c494cd06f 100644 --- a/docs/guide/install.txt +++ b/docs/guide/install.txt @@ -29,6 +29,6 @@ __ http://www.django-rest-framework.org/ -* **Python**: 2.7, 3.4, 3.5, 3.6 -* **Django**: 1.10, 1.11 +* **Python**: 3.4, 3.5, 3.6 +* **Django**: 1.11, 2.0b * **DRF**: 3.7 diff --git a/setup.py b/setup.py index 02e1c9253..d939cdfa2 100644 --- a/setup.py +++ b/setup.py @@ -43,14 +43,9 @@ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Framework :: Django', - 'Framework :: Django :: 1.8', - 'Framework :: Django :: 1.10', 'Framework :: Django :: 1.11', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff --git a/tests/models.py b/tests/models.py index 848e7f36e..aadd4f3e6 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,8 +1,5 @@ -from __future__ import absolute_import, unicode_literals - from django import forms from django.db import models -from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ REGULAR = 0 @@ -42,7 +39,6 @@ def formfield(self, **kwargs): return super(SubnetMaskField, self).formfield(**defaults) -@python_2_unicode_compatible class User(models.Model): username = models.CharField(_('username'), max_length=255) first_name = SubCharField(max_length=100) @@ -58,7 +54,6 @@ def __str__(self): return self.username -@python_2_unicode_compatible class ManagerGroup(models.Model): users = models.ManyToManyField(User, limit_choices_to={'is_active': True}, @@ -72,7 +67,6 @@ def __str__(self): return self.manager.name + ' group' -@python_2_unicode_compatible class AdminUser(User): class Meta: proxy = True @@ -81,7 +75,6 @@ def __str__(self): return "%s (ADMIN)" % self.username -@python_2_unicode_compatible class Comment(models.Model): text = models.TextField() author = models.ForeignKey(User, related_name='comments', on_delete=models.CASCADE) @@ -104,7 +97,6 @@ def __str__(self): return "Anonymous on %s" % self.published -@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=100) price = models.DecimalField(max_digits=6, decimal_places=2) @@ -132,7 +124,6 @@ class NetworkSetting(models.Model): cidr = models.CharField(max_length=18, blank=True, verbose_name="CIDR") -@python_2_unicode_compatible class Company(models.Model): name = models.CharField(max_length=100) @@ -143,7 +134,6 @@ class Meta: ordering = ['name'] -@python_2_unicode_compatible class Location(models.Model): company = models.ForeignKey(Company, related_name='locations', on_delete=models.CASCADE) name = models.CharField(max_length=100) diff --git a/tests/rest_framework/test_backends.py b/tests/rest_framework/test_backends.py index c5671cd05..b17cf72f3 100644 --- a/tests/rest_framework/test_backends.py +++ b/tests/rest_framework/test_backends.py @@ -1,8 +1,4 @@ -from __future__ import unicode_literals - -import datetime import warnings -from decimal import Decimal from unittest import skipIf from django.db.models import BooleanField diff --git a/tests/rest_framework/test_integration.py b/tests/rest_framework/test_integration.py index eee716736..c5bb2998d 100644 --- a/tests/rest_framework/test_integration.py +++ b/tests/rest_framework/test_integration.py @@ -1,11 +1,10 @@ -from __future__ import unicode_literals - import datetime from decimal import Decimal from django.conf.urls import url from django.test import TestCase from django.test.utils import override_settings +from django.urls import reverse from django.utils.dateparse import parse_date from rest_framework import generics, serializers, status from rest_framework.test import APIRequestFactory @@ -20,15 +19,6 @@ FilterableItem ) -try: - from django.urls import reverse -except ImportError: - # Django < 1.10 compatibility - from django.core.urlresolvers import reverse - - - - factory = APIRequestFactory() diff --git a/tests/test_fields.py b/tests/test_fields.py index 21c68d166..dd36c5399 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import decimal from datetime import datetime, time, timedelta, tzinfo diff --git a/tests/test_filtering.py b/tests/test_filtering.py index c5225ba53..eb8cd5f5a 100644 --- a/tests/test_filtering.py +++ b/tests/test_filtering.py @@ -1,13 +1,10 @@ -from __future__ import absolute_import, unicode_literals - import datetime import mock import unittest -import django from django import forms from django.test import TestCase, override_settings -from django.utils import six, timezone +from django.utils import timezone from django.utils.timezone import now from django_filters.exceptions import FieldLookupError @@ -301,7 +298,6 @@ def test_filtering(self): User.objects.create(username='aaron', status=2) User.objects.create(username='carl', status=0) - class F(FilterSet): status = TypedMultipleChoiceFilter(choices=STATUS_CHOICES, coerce=lambda x: x[0:2]) @@ -334,7 +330,7 @@ def test_filtering(self): today = now().date() timestamp = now().time().replace(microsecond=0) last_week = today - datetime.timedelta(days=7) - check_date = six.text_type(last_week) + check_date = str(last_week) u = User.objects.create(username='alex') Comment.objects.create(author=u, time=timestamp, date=today) Comment.objects.create(author=u, time=timestamp, date=last_week) @@ -358,7 +354,7 @@ def test_filtering(self): now_time = now().time().replace(microsecond=0) ten_min_ago = (now() - datetime.timedelta(minutes=10)) fixed_time = ten_min_ago.time().replace(microsecond=0) - check_time = six.text_type(fixed_time) + check_time = str(fixed_time) u = User.objects.create(username='alex') Comment.objects.create(author=u, time=now_time, date=today) Comment.objects.create(author=u, time=fixed_time, date=today) @@ -389,7 +385,7 @@ def test_filtering(self): tz = timezone.get_current_timezone() # make naive, like a browser would send local_ten_min_ago = timezone.make_naive(ten_min_ago, tz) - check_dt = six.text_type(local_ten_min_ago) + check_dt = str(local_ten_min_ago) class F(FilterSet): class Meta: @@ -941,7 +937,6 @@ class Meta: 'published_1': '2016-01-03'}) self.assertEqual(len(results.qs), 3) - @unittest.skipIf(django.VERSION < (1, 9), 'version doesnt supports is_dst parameter for make_aware') @override_settings(TIME_ZONE='America/Sao_Paulo') def test_filtering_dst_start_midnight(self): tz = timezone.get_default_timezone() @@ -962,7 +957,6 @@ class Meta: 'published_1': '2017-10-15'}) self.assertEqual(len(results.qs), 2) - @unittest.skipIf(django.VERSION < (1, 9), 'version doesnt supports is_dst parameter for make_aware') @override_settings(TIME_ZONE='America/Sao_Paulo') def test_filtering_dst_ends_midnight(self): tz = timezone.get_default_timezone() @@ -983,7 +977,6 @@ class Meta: 'published_1': '2017-02-18'}) self.assertEqual(len(results.qs), 2) - @unittest.skipIf(django.VERSION < (1, 9), 'version doesnt supports is_dst parameter for make_aware') @override_settings(TIME_ZONE='Europe/Paris') def test_filtering_dst_start(self): tz = timezone.get_default_timezone() @@ -1005,7 +998,6 @@ class Meta: 'published_1': '2017-3-26'}) self.assertEqual(len(results.qs), 3) - @unittest.skipIf(django.VERSION < (1, 9), 'version doesnt supports is_dst parameter for make_aware') @override_settings(TIME_ZONE='Europe/Paris') def test_filtering_dst_end(self): tz = timezone.get_default_timezone() @@ -1667,7 +1659,6 @@ class Meta: # use naive datetimes, as pytz is required to perform # date lookups when timezones are involved. @override_settings(USE_TZ=False) -@unittest.skipIf(django.VERSION < (1, 9), "version does not support transformed lookup expressions") class TransformedQueryExpressionFilterTests(TestCase): def test_filtering(self): diff --git a/tests/test_filters.py b/tests/test_filters.py index 713230a80..d3a16517a 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - import inspect import mock from collections import OrderedDict diff --git a/tests/test_filterset.py b/tests/test_filterset.py index 976a205a9..0c5ba7cca 100644 --- a/tests/test_filterset.py +++ b/tests/test_filterset.py @@ -1,9 +1,6 @@ -from __future__ import absolute_import, unicode_literals - import mock import unittest -import django from django.core.exceptions import ValidationError from django.db import models from django.test import TestCase, override_settings @@ -195,7 +192,6 @@ def test_m2m_field_with_through_model(self): self.assertIsNotNone(result.extra['queryset']) self.assertEqual(result.extra['queryset'].model, Worker) - @unittest.skipIf(django.VERSION < (1, 9), "version does not support transformed lookup expressions") def test_transformed_lookup_expr(self): f = Comment._meta.get_field('date') result = FilterSet.filter_for_field(f, 'date', 'year__gte') diff --git a/tests/test_forms.py b/tests/test_forms.py index 0932f35e6..8b1dfbe22 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from django import forms from django.test import TestCase, override_settings diff --git a/tests/test_utils.py b/tests/test_utils.py index 138ef654f..dd7c6438d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,5 @@ import datetime -import unittest -import django from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.fields.related import ForeignObjectRel @@ -24,7 +22,6 @@ ) from .models import ( - Account, Article, Book, Business, @@ -112,7 +109,6 @@ def test_resolve_forward_related_lookups(self): self.assertIsInstance(field, models.ManyToManyField) self.assertEqual(lookup, term) - @unittest.skipIf(django.VERSION < (1, 9), "version does not reverse lookups") def test_resolve_reverse_related_lookups(self): """ Check that lookups can be resolved for related fields @@ -134,7 +130,6 @@ def test_resolve_reverse_related_lookups(self): self.assertIsInstance(field, models.ManyToManyRel) self.assertEqual(lookup, term) - @unittest.skipIf(django.VERSION < (1, 9), "version does not support transformed lookup expressions") def test_resolve_transformed_lookups(self): """ Check that chained field transforms are correctly resolved. @@ -186,7 +181,6 @@ def test_resolve_transformed_lookups(self): self.assertIsInstance(field, models.IntegerField) self.assertEqual(resolved_lookup, lookup) - @unittest.skipIf(django.VERSION < (1, 9), "version does not support transformed lookup expressions") def test_resolve_implicit_exact_lookup(self): # Use a DateTimeField, so we can check multiple transforms. # eg, date__year__gte @@ -335,14 +329,12 @@ def test_field_all_caps(self): class HandleTimezone(TestCase): - @unittest.skipIf(django.VERSION < (1, 9), 'version doesnt supports is_dst parameter for make_aware') @override_settings(TIME_ZONE='America/Sao_Paulo') def test_handle_dst_ending(self): dst_ending_date = datetime.datetime(2017, 2, 18, 23, 59, 59, 999999) handled = handle_timezone(dst_ending_date, False) self.assertEqual(handled, get_default_timezone().localize(dst_ending_date, False)) - @unittest.skipIf(django.VERSION < (1, 9), 'version doesnt supports is_dst parameter for make_aware') @override_settings(TIME_ZONE='America/Sao_Paulo') def test_handle_dst_starting(self): dst_starting_date = datetime.datetime(2017, 10, 15, 0, 0, 0, 0) diff --git a/tests/test_views.py b/tests/test_views.py index e8fdbc718..9204e9a33 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, override_settings from django.test.client import RequestFactory diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 78ff92d30..e170d2c58 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from django.forms import Select, TextInput from django.test import TestCase diff --git a/tests/urls.py b/tests/urls.py index 64a0c0600..8a7da0627 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - from django.conf.urls import url from django_filters.views import FilterView, object_filter diff --git a/tox.ini b/tox.ini index 70cabd959..dc54f9e2b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - {py27,py34,py35}-django110, - {py27,py35,py36}-django111, + {py27,py34,py35,py36}-django111, + {py34,py35,py36}-django20, {py35,py36}-latest, isort, warnings, @@ -17,8 +17,8 @@ ignore_outcome = setenv = PYTHONDONTWRITEBYTECODE=1 deps = - django110: django>=1.10.0,<1.11.0 django111: django>=1.11.0,<2.0 + django20: django>=2.0b1,<2.1 djangorestframework>=3.7,<3.8 latest: {[latest]deps} -rrequirements/test-ci.txt