diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 104744b..a3c4317 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -1400,7 +1400,7 @@ class BaseDatabaseIntrospection(object):
     def sequence_list(self):
         "Returns a list of information about all DB sequences for all models in all apps."
         from django.apps import apps
-        from django.db import models, router
+        from django.db import router
 
         sequence_list = []
 
@@ -1411,7 +1411,7 @@ class BaseDatabaseIntrospection(object):
                 if model._meta.swapped:
                     continue
                 for f in model._meta.local_fields:
-                    if isinstance(f, models.AutoField):
+                    if f.get_internal_type() == 'AutoField':
                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
                         break  # Only one AutoField is allowed per model, so don't bother continuing.
 
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index 9f275b7..c7a162c 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -444,12 +444,11 @@ WHEN (new.%(col_name)s IS NULL)
         return sql
 
     def sequence_reset_sql(self, style, model_list):
-        from django.db import models
         output = []
         query = _get_sequence_reset_sql()
         for model in model_list:
             for f in model._meta.local_fields:
-                if isinstance(f, models.AutoField):
+                if f.get_internal_type() == 'AutoField':
                     table_name = self.quote_name(model._meta.db_table)
                     sequence_name = self._get_sequence_name(model._meta.db_table)
                     column_name = self.quote_name(f.column)
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
index 3447bfe..181d8c4 100644
--- a/django/db/backends/postgresql_psycopg2/operations.py
+++ b/django/db/backends/postgresql_psycopg2/operations.py
@@ -135,7 +135,6 @@ class DatabaseOperations(BaseDatabaseOperations):
             return "TABLESPACE %s" % self.quote_name(tablespace)
 
     def sequence_reset_sql(self, style, model_list):
-        from django.db import models
         output = []
         qn = self.quote_name
         for model in model_list:
@@ -146,7 +145,7 @@ class DatabaseOperations(BaseDatabaseOperations):
             # and column name (available since PostgreSQL 8)
 
             for f in model._meta.local_fields:
-                if isinstance(f, models.AutoField):
+                if f.get_internal_type() == 'AutoField':
                     output.append(
                         "%s setval(pg_get_serial_sequence('%s','%s'), "
                         "coalesce(max(%s), 1), max(%s) %s null) %s %s;" % (
diff --git a/django/db/models/base.py b/django/db/models/base.py
index d0c1b7a..e68fb20 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -16,7 +16,7 @@ from django.db import (router, connections, transaction, DatabaseError,
 from django.db.models import signals
 from django.db.models.constants import LOOKUP_SEP
 from django.db.models.deletion import Collector
-from django.db.models.fields import AutoField, FieldDoesNotExist
+from django.db.models.fields import FieldDoesNotExist
 from django.db.models.fields.related import (ForeignObjectRel, ManyToOneRel,
     OneToOneField, add_lazy_relation)
 from django.db.models.manager import ensure_default_manager
@@ -797,7 +797,7 @@ class Model(six.with_metaclass(ModelBase)):
 
             fields = meta.local_concrete_fields
             if not pk_set:
-                fields = [f for f in fields if not isinstance(f, AutoField)]
+                fields = [f for f in fields if not f.get_internal_type() == "AutoField"]
 
             update_pk = bool(meta.has_auto_field and not pk_set)
             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index a2f4b95..dc563fb 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -8,7 +8,7 @@ from django.db import connection, connections, router, transaction
 from django.db.backends import utils
 from django.db.models import signals, Q
 from django.db.models.deletion import SET_NULL, SET_DEFAULT, CASCADE
-from django.db.models.fields import (AutoField, Field, IntegerField,
+from django.db.models.fields import (Field, IntegerField,
     PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist)
 from django.db.models.lookups import IsNull
 from django.db.models.related import RelatedObject, PathInfo
@@ -1860,7 +1860,7 @@ class ForeignKey(ForeignObject):
         # If the database needs similar types for key fields however, the only
         # thing we can do is making AutoField an IntegerField.
         rel_field = self.related_field
-        if (isinstance(rel_field, AutoField) or
+        if (rel_field.get_internal_type() == 'AutoField' or
                 (not connection.features.related_fields_match_type and
                 isinstance(rel_field, (PositiveIntegerField,
                                        PositiveSmallIntegerField)))):
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 42dfd47..b17a8fc 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -12,7 +12,7 @@ from django.core import exceptions
 from django.db import (connections, router, transaction, IntegrityError,
     DJANGO_VERSION_PICKLE_KEY)
 from django.db.models.constants import LOOKUP_SEP
-from django.db.models.fields import AutoField, Empty
+from django.db.models.fields import Empty
 from django.db.models.query_utils import (Q, select_related_descend,
     deferred_class_factory, InvalidQuery)
 from django.db.models.deletion import Collector
@@ -424,7 +424,7 @@ class QuerySet(object):
                 if objs_with_pk:
                     self._batched_insert(objs_with_pk, fields, batch_size)
                 if objs_without_pk:
-                    fields = [f for f in fields if not isinstance(f, AutoField)]
+                    fields = [f for f in fields if not f.get_internal_type() == 'AutoField']
                     self._batched_insert(objs_without_pk, fields, batch_size)
 
         return objs
diff --git a/django/forms/models.py b/django/forms/models.py
index 7ca4954..58290ff 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -45,7 +45,7 @@ def construct_instance(form, instance, fields=None, exclude=None):
     cleaned_data = form.cleaned_data
     file_field_list = []
     for f in opts.fields:
-        if not f.editable or isinstance(f, models.AutoField) \
+        if not f.editable or f.get_internal_type() == 'AutoField' \
                 or f.name not in cleaned_data:
             continue
         if fields is not None and f.name not in fields:
@@ -773,7 +773,7 @@ class BaseModelFormSet(BaseFormSet):
 
     def add_fields(self, form, index):
         """Add a hidden field for the object's primary key."""
-        from django.db.models import AutoField, OneToOneField, ForeignKey
+        from django.db.models import OneToOneField, ForeignKey
         self._pk_field = pk = self.model._meta.pk
         # If a pk isn't editable, then it won't be on the form, so we need to
         # add it here so we can tell which object is which when we get the
@@ -782,7 +782,8 @@ class BaseModelFormSet(BaseFormSet):
         # True, so check for that as well.
 
         def pk_is_not_editable(pk):
-            return ((not pk.editable) or (pk.auto_created or isinstance(pk, AutoField))
+            return ((not pk.editable)
+                or (pk.auto_created or pk.get_internal_type == 'AutoField')
                 or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
         if pk_is_not_editable(pk) or pk.name not in form.fields:
             if form.is_bound:
diff --git a/tests/field_subclassing/fields.py b/tests/field_subclassing/fields.py
index b94b237..a738433 100644
--- a/tests/field_subclassing/fields.py
+++ b/tests/field_subclassing/fields.py
@@ -3,11 +3,13 @@ from __future__ import unicode_literals
 import json
 import warnings
 
+from django.core import exceptions
 from django.db import models
 from django.utils.encoding import force_text
 from django.utils import six
 from django.utils.deprecation import RemovedInDjango20Warning
 from django.utils.encoding import python_2_unicode_compatible
+from django.utils.translation import ugettext_lazy as _
 
 
 # Catch warning about subfieldbase  -- remove in Django 2.0
@@ -94,3 +96,53 @@ class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)):
 class CustomTypedField(models.TextField):
     def db_type(self, connection):
         return 'custom_field'
+
+
+class CustomAutoField(models.Field):
+    description = _("Integer")
+
+    empty_strings_allowed = False
+    default_error_messages = {
+        'invalid': _("'%(value)s' value must be an integer."),
+    }
+
+    def __init__(self, *args, **kwargs):
+        kwargs['blank'] = True
+        super(CustomAutoField, self).__init__(*args, **kwargs)
+
+    def get_internal_type(self):
+        return "AutoField"
+
+    def to_python(self, value):
+        if value is None:
+            return value
+        try:
+            return int(value)
+        except (TypeError, ValueError):
+            raise exceptions.ValidationError(
+                self.error_messages['invalid'],
+                code='invalid',
+                params={'value': value},
+            )
+
+    def get_db_prep_value(self, value, connection, prepared=False):
+        if not prepared:
+            value = self.get_prep_value(value)
+            value = connection.ops.validate_autopk_value(value)
+        return value
+
+    def get_prep_value(self, value):
+        value = super(CustomAutoField, self).get_prep_value(value)
+        if value is None:
+            return None
+        return int(value)
+
+    def contribute_to_class(self, cls, name, **kwargs):
+        assert not cls._meta.has_auto_field, \
+            "A model can't have more than one AutoField."
+        super(CustomAutoField, self).contribute_to_class(cls, name, **kwargs)
+        cls._meta.has_auto_field = True
+        cls._meta.auto_field = self
+
+    def formfield(self, **kwargs):
+        return None
diff --git a/tests/field_subclassing/models.py b/tests/field_subclassing/models.py
index 66e765a..6107228 100644
--- a/tests/field_subclassing/models.py
+++ b/tests/field_subclassing/models.py
@@ -4,7 +4,7 @@ Tests for field subclassing.
 from django.db import models
 from django.utils.encoding import force_text
 
-from .fields import Small, SmallField, SmallerField, JSONField
+from .fields import Small, SmallField, SmallerField, JSONField, CustomAutoField
 from django.utils.encoding import python_2_unicode_compatible
 
 
@@ -33,3 +33,7 @@ class ChoicesModel(models.Model):
 
 class DataModel(models.Model):
     data = JSONField()
+
+
+class CustomAutoFieldModel(models.Model):
+    id = CustomAutoField(primary_key=True)
diff --git a/tests/field_subclassing/tests.py b/tests/field_subclassing/tests.py
index 9e40d92..fac1997 100644
--- a/tests/field_subclassing/tests.py
+++ b/tests/field_subclassing/tests.py
@@ -7,7 +7,7 @@ from django.db import connection
 from django.test import TestCase
 
 from .fields import Small, CustomTypedField
-from .models import ChoicesModel, DataModel, MyModel, OtherModel
+from .models import ChoicesModel, DataModel, MyModel, OtherModel, CustomAutoFieldModel
 
 
 class CustomField(TestCase):
@@ -122,6 +122,13 @@ class CustomField(TestCase):
         with self.assertRaises(exceptions.ValidationError):
             o.full_clean()
 
+    def test_auto_field(self):
+        o = CustomAutoFieldModel()
+        self.assertEqual(o.pk, None)
+        o.save()
+        self.assertNotEqual(o.pk, None)
+        self.assertTrue(isinstance(o.pk, int))
+
 
 class TestDbType(TestCase):
 
