diff --git a/django_filters/filters.py b/django_filters/filters.py
index c05ef4e72..e64fd100a 100644
--- a/django_filters/filters.py
+++ b/django_filters/filters.py
@@ -6,7 +6,6 @@
 from django.db.models.constants import LOOKUP_SEP
 from django.db.models.sql.constants import QUERY_TERMS
 from django.forms.utils import pretty_name
-from django.utils import six
 from django.utils.itercompat import is_iterable
 from django.utils.timezone import now
 from django.utils.translation import ugettext_lazy as _
@@ -184,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
@@ -473,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)
@@ -658,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 de5ac8ecb..a33540ff3 100644
--- a/django_filters/filterset.py
+++ b/django_filters/filterset.py
@@ -5,7 +5,6 @@
 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 .conf import settings
 from .constants import ALL_FIELDS, STRICTNESS
@@ -185,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
@@ -200,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)
@@ -414,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/backends.py b/django_filters/rest_framework/backends.py
index 571f2d44f..eeba17361 100644
--- a/django_filters/rest_framework/backends.py
+++ b/django_filters/rest_framework/backends.py
@@ -1,7 +1,6 @@
 import warnings
 
 from django.template import loader
-from django.utils import six
 
 from . import filters, filterset
 from .. import compat
@@ -71,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/utils.py b/django_filters/utils.py
index 989ee554e..f94c3aa50 100644
--- a/django_filters/utils.py
+++ b/django_filters/utils.py
@@ -9,10 +9,9 @@
 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.timezone import make_aware
 from django.utils.translation import ugettext as _
 
 from .exceptions import FieldLookupError
@@ -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/widgets.py b/django_filters/widgets.py
index c0d13ff8f..ba1d2c16c 100644
--- a/django_filters/widgets.py
+++ b/django_filters/widgets.py
@@ -9,7 +9,6 @@
 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 _
 
 
@@ -169,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 {
@@ -184,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)
@@ -232,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/tests/test_filtering.py b/tests/test_filtering.py
index 05fe6a5de..eb8cd5f5a 100644
--- a/tests/test_filtering.py
+++ b/tests/test_filtering.py
@@ -4,7 +4,7 @@
 
 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
@@ -298,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])
 
@@ -331,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)
@@ -355,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)
@@ -386,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: