diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 7a42e94..bb11b3d 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -9,6 +9,7 @@ from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_ob
 from django.contrib import messages
 from django.views.decorators.csrf import csrf_protect
 from django.core.exceptions import PermissionDenied, ValidationError
+from django.core.paginator import Paginator
 from django.db import models, transaction, router
 from django.db.models.fields import BLANK_CHOICE_DASH
 from django.http import Http404, HttpResponse, HttpResponseRedirect
@@ -215,6 +216,7 @@ class ModelAdmin(BaseModelAdmin):
     date_hierarchy = None
     save_as = False
     save_on_top = False
+    paginator_class = Paginator
     inlines = []
 
     # Custom templates (designed to be over-ridden in subclasses)
@@ -975,8 +977,9 @@ class ModelAdmin(BaseModelAdmin):
 
         ChangeList = self.get_changelist(request)
         try:
-            cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter,
-                self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self)
+            cl = ChangeList(request, self.model, list_display, self.list_display_links,
+                self.list_filter, self.date_hierarchy, self.search_fields,
+                self.list_select_related, self.list_per_page, self.list_editable, self.paginator_class, self)
         except IncorrectLookupParameters:
             # Wacky lookup parameters were given, so redirect to the main
             # changelist page, without parameters, and pass an 'invalid=1'
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 606388c..19c7399 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -26,7 +26,7 @@ ERROR_FLAG = 'e'
 EMPTY_CHANGELIST_VALUE = '(None)'
 
 class ChangeList(object):
-    def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin):
+    def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, paginator_class, model_admin):
         self.model = model
         self.opts = model._meta
         self.lookup_opts = self.opts
@@ -40,6 +40,7 @@ class ChangeList(object):
         self.list_per_page = list_per_page
         self.list_editable = list_editable
         self.model_admin = model_admin
+        self.paginator_class = paginator_class
 
         # Get search parameters from the query string.
         try:
@@ -94,7 +95,7 @@ class ChangeList(object):
         return '?%s' % urlencode(p)
 
     def get_results(self, request):
-        paginator = Paginator(self.query_set, self.list_per_page)
+        paginator = self.get_paginator(self.query_set, self.list_per_page)
         # Get the number of objects, with admin filters applied.
         result_count = paginator.count
 
@@ -244,3 +245,6 @@ class ChangeList(object):
 
     def url_for_result(self, result):
         return "%s/" % quote(getattr(result, self.pk_attname))
+
+    def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True):
+        return self.paginator_class(queryset, per_page, orphans, allow_empty_first_page)
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 9928118..cb07cb6 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -516,6 +516,16 @@ subclass::
         Django will only honor the first element in the list/tuple; any others
         will be ignored.
 
+.. attribute:: ModelAdmin.paginator_class
+
+    .. versionadded:: 1.3
+
+    The paginator class to be used for pagination. By default,
+    :class:`django.core.paginator.Paginator` is used. If the custom paginator
+    class doesn't have the same constructor interface as
+    :class:`django.core.paginator.Paginator`, you will also need to
+    provide an implementation for :meth:`MultipleObjectMixin.get_paginator`.
+
 .. attribute:: ModelAdmin.prepopulated_fields
 
     Set ``prepopulated_fields`` to a dictionary mapping field names to the
@@ -945,6 +955,13 @@ templates used by the :class:`ModelAdmin` views:
     using the :mod:`django.contrib.messages` backend. See the
     :ref:`custom ModelAdmin example <custom-admin-action>`.
 
+.. method:: ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
+
+    .. versionadded:: 1.3
+
+    Returns an instance of the paginator to use for this view. By default,
+    instantiates an instance of :attr:`paginator_class`.
+
 Other methods
 ~~~~~~~~~~~~~
 
diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py
index 96b36f8..b94a79b 100644
--- a/tests/regressiontests/admin_changelist/tests.py
+++ b/tests/regressiontests/admin_changelist/tests.py
@@ -1,10 +1,13 @@
 from django.contrib import admin
 from django.contrib.admin.options import IncorrectLookupParameters
 from django.contrib.admin.views.main import ChangeList
+from django.core.paginator import Paginator
 from django.template import Context, Template
 from django.test import TransactionTestCase
+
 from regressiontests.admin_changelist.models import Child, Parent
 
+
 class ChangeListTests(TransactionTestCase):
     def test_select_related_preserved(self):
         """
@@ -14,7 +17,8 @@ class ChangeListTests(TransactionTestCase):
         m = ChildAdmin(Child, admin.site)
         cl = ChangeList(MockRequest(), Child, m.list_display, m.list_display_links,
                 m.list_filter, m.date_hierarchy, m.search_fields,
-                m.list_select_related, m.list_per_page, m.list_editable, m)
+                m.list_select_related, m.list_per_page, m.list_editable,
+                m.paginator_class, m)
         self.assertEqual(cl.query_set.query.select_related, {'parent': {'name': {}}})
 
     def test_result_list_html(self):
@@ -28,7 +32,8 @@ class ChangeListTests(TransactionTestCase):
         m = ChildAdmin(Child, admin.site)
         cl = ChangeList(request, Child, m.list_display, m.list_display_links,
                 m.list_filter, m.date_hierarchy, m.search_fields,
-                m.list_select_related, m.list_per_page, m.list_editable, m)
+                m.list_select_related, m.list_per_page, m.list_editable,
+                m.paginator_class, m)
         cl.formset = None
         template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
         context = Context({'cl': cl})
@@ -57,7 +62,8 @@ class ChangeListTests(TransactionTestCase):
         m.list_editable = ['name']
         cl = ChangeList(request, Child, m.list_display, m.list_display_links,
                 m.list_filter, m.date_hierarchy, m.search_fields,
-                m.list_select_related, m.list_per_page, m.list_editable, m)
+                m.list_select_related, m.list_per_page, m.list_editable,
+                m.paginator_class, m)
         FormSet = m.get_changelist_formset(request)
         cl.formset = FormSet(queryset=cl.result_list)
         template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
@@ -91,7 +97,28 @@ class ChangeListTests(TransactionTestCase):
         self.assertRaises(IncorrectLookupParameters, lambda: \
             ChangeList(request, Child, m.list_display, m.list_display_links,
                     m.list_filter, m.date_hierarchy, m.search_fields,
-                    m.list_select_related, m.list_per_page, m.list_editable, m))
+                    m.list_select_related, m.list_per_page, m.list_editable,
+                    m.paginator_class, m))
+
+    def test_custom_paginator(self):
+        new_parent = Parent.objects.create(name='parent')
+        for i in range(200):
+            new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
+
+        request = MockRequest()
+        m = ChildAdmin(Child, admin.site)
+        m.list_display = ['id', 'name', 'parent']
+        m.list_display_links = ['id']
+        m.list_editable = ['name']
+        m.paginator_class = CustomPaginator
+
+        cl = ChangeList(request, Child, m.list_display, m.list_display_links,
+                m.list_filter, m.date_hierarchy, m.search_fields,
+                m.list_select_related, m.list_per_page, m.list_editable,
+                m.paginator_class, m)
+
+        cl.get_results(request)
+        self.assertIsInstance(cl.paginator, CustomPaginator)
 
 
 class ChildAdmin(admin.ModelAdmin):
@@ -99,5 +126,15 @@ class ChildAdmin(admin.ModelAdmin):
     def queryset(self, request):
         return super(ChildAdmin, self).queryset(request).select_related("parent__name")
 
+
 class MockRequest(object):
     GET = {}
+
+
+class CustomPaginator(Paginator):
+    def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
+        super(CustomPaginator, self).__init__(
+            queryset,
+            5,
+            orphans=2,
+            allow_empty_first_page=allow_empty_first_page)
