Index: tests/regressiontests/model_fields/tests.py
===================================================================
diff --git a/django/trunk/tests/regressiontests/model_fields/tests.py b/django/trunk/tests/regressiontests/model_fields/tests.py
--- a/django/trunk/tests/regressiontests/model_fields/tests.py	(revision 17703)
+++ b/django/trunk/tests/regressiontests/model_fields/tests.py	(working copy)
@@ -11,7 +11,8 @@
 from django.utils import unittest
 
 from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post,
-    NullBooleanModel, BooleanModel, Document, RenamedField)
+    NullBooleanModel, BooleanModel, Document, RenamedField,
+    CustomIntegerFormField)
 
 # If PIL available, do these tests.
 if Image:
@@ -223,6 +224,16 @@
         self.assertEqual(Whiz(c=None).get_c_display(), None)    # Blank value
         self.assertEqual(Whiz(c='').get_c_display(), '')        # Empty value
 
+    def test_choices_formfield_overrides(self):
+        whiz_meta = Whiz(c=1)._meta
+        field = whiz_meta.get_field_by_name('c')[0]
+        self.assertIs(type(field.formfield()),
+                forms.TypedChoiceField)
+        self.assertIs(type(whiz_meta.get_field_by_name('d')[0].formfield()),
+                CustomIntegerFormField)
+        self.assertIs(type(whiz_meta.get_field_by_name('ts')[0].formfield()),
+                forms.DateTimeField)
+
 class SlugFieldTests(test.TestCase):
     def test_slugfield_max_length(self):
         """
Index: tests/regressiontests/model_fields/models.py
===================================================================
diff --git a/django/trunk/tests/regressiontests/model_fields/models.py b/django/trunk/tests/regressiontests/model_fields/models.py
--- a/django/trunk/tests/regressiontests/model_fields/models.py	(revision 17703)
+++ b/django/trunk/tests/regressiontests/model_fields/models.py	(working copy)
@@ -13,6 +13,7 @@
     except ImportError:
         Image = None
 
+from django import forms
 from django.core.files.storage import FileSystemStorage
 from django.db import models
 from django.db.models.fields.files import ImageFieldFile, ImageField
@@ -29,6 +30,20 @@
     b = models.CharField(max_length=10)
     a = models.ForeignKey(Foo, default=get_foo)
 
+class CustomIntegerFormField(forms.IntegerField):
+    override_for_choices = False
+
+    def __init__(self, *args, **kwargs):
+        if 'choices' in kwargs:
+            del kwargs['choices']
+        super(CustomIntegerFormField, self).__init__(*args, **kwargs)
+
+class CustomIntegerField(models.IntegerField):
+    def formfield(self, **kwargs):
+        if 'form_class' not in kwargs:
+            kwargs['form_class'] = CustomIntegerFormField
+        return super(CustomIntegerField, self).formfield(**kwargs)
+
 class Whiz(models.Model):
     CHOICES = (
         ('Group 1', (
@@ -44,6 +59,8 @@
         (0,'Other'),
     )
     c = models.IntegerField(choices=CHOICES, null=True)
+    d = CustomIntegerField(choices=CHOICES, default=4)
+    ts = models.DateTimeField(auto_now_add=True)
 
 class BigD(models.Model):
     d = models.DecimalField(max_digits=38, decimal_places=30)
Index: django/db/models/fields/__init__.py
===================================================================
diff --git a/django/trunk/django/db/models/fields/__init__.py b/django/trunk/django/db/models/fields/__init__.py
--- a/django/trunk/django/db/models/fields/__init__.py	(revision 17704)
+++ b/django/trunk/django/db/models/fields/__init__.py	(working copy)
@@ -471,18 +471,19 @@
             include_blank = (self.blank or
                              not (self.has_default() or 'initial' in kwargs))
             defaults['choices'] = self.get_choices(include_blank=include_blank)
-            defaults['coerce'] = self.to_python
-            if self.null:
-                defaults['empty_value'] = None
-            form_class = forms.TypedChoiceField
-            # Many of the subclass-specific formfield arguments (min_value,
-            # max_value) don't apply for choice fields, so be sure to only pass
-            # the values that TypedChoiceField will understand.
-            for k in kwargs.keys():
-                if k not in ('coerce', 'empty_value', 'choices', 'required',
-                             'widget', 'label', 'initial', 'help_text',
-                             'error_messages', 'show_hidden_initial'):
-                    del kwargs[k]
+            if form_class.override_for_choices:
+                defaults['coerce'] = self.to_python
+                if self.null:
+                    defaults['empty_value'] = None
+                form_class = forms.TypedChoiceField
+                # Many of the subclass-specific formfield arguments (min_value,
+                # max_value) don't apply for choice fields, so be sure to only
+                # pass the values that TypedChoiceField will understand.
+                for k in kwargs.keys():
+                    if k not in ('coerce', 'empty_value', 'choices', 'required',
+                                'widget', 'label', 'initial', 'help_text',
+                                'error_messages', 'show_hidden_initial'):
+                        del kwargs[k]
         defaults.update(kwargs)
         return form_class(**defaults)
 
Index: django/forms/fields.py
===================================================================
diff --git a/django/trunk/django/forms/fields.py b/django/trunk/django/forms/fields.py
--- a/django/trunk/django/forms/fields.py	(revision 17704)
+++ b/django/trunk/django/forms/fields.py	(working copy)
@@ -50,6 +50,7 @@
         'required': _(u'This field is required.'),
         'invalid': _(u'Enter a valid value.'),
     }
+    override_for_choices = False
 
     # Tracks each time a Field instance is created. Used to retain order.
     creation_counter = 0
@@ -182,6 +183,8 @@
         return result
 
 class CharField(Field):
+    override_for_choices = True
+
     def __init__(self, max_length=None, min_length=None, *args, **kwargs):
         self.max_length, self.min_length = max_length, min_length
         super(CharField, self).__init__(*args, **kwargs)
@@ -209,6 +212,7 @@
         'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
         'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'),
     }
+    override_for_choices = True
 
     def __init__(self, max_value=None, min_value=None, *args, **kwargs):
         self.max_value, self.min_value = max_value, min_value
@@ -665,6 +669,7 @@
     cleaned to None.
     """
     widget = NullBooleanSelect
+    override_for_choices = True
 
     def to_python(self, value):
         """
