diff --git a/medley/abcs/fields.py b/medley/abcs/fields.py
new file mode 100644
index 0000000..bc897b4
--- /dev/null
+++ b/medley/abcs/fields.py
@@ -0,0 +1,40 @@
+import django
+from django import forms
+from django.utils.functional import SimpleLazyObject
+
+class SimpleLazyList(SimpleLazyObject):
+    def __iter__(self):
+        if self._wrapped is None: self._setup()
+        return iter(self._wrapped)
+
+    def _setup(self):
+        self._wrapped = list(self._setupfunc())
+
+class CallableChoicesMixin(object):
+    """ A ChoiceField that can take a callable
+    """
+    def _get_choices(self):
+        return self._choices
+
+    def _set_choices(self, value):
+        if callable(value):
+            list_value = SimpleLazyList(value)
+        else:
+            list_value = list(value)
+        self._choices = self.widget.choices = list_value
+
+    choices = property(_get_choices, _set_choices)
+
+class CallableChoiceField(CallableChoicesMixin, forms.ChoiceField):
+    pass
+
+class CallableMultipleChoiceField(CallableChoicesMixin, forms.MultipleChoiceField):
+    pass
+
+class CallableTypedChoiceField(CallableChoicesMixin, forms.TypedChoiceField):
+    pass
+
+# FIXME: remove conditional once 1.3 upgrade branch lands.
+if django.VERSION >= (1, 3, 0):
+    class CallableTypedMultipleChoiceField(CallableChoicesMixin, forms.TypedMultipleChoiceField):
+        pass
