app/django/forms/models.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 """
       
     2 Helper functions for creating Form classes from Django models
       
     3 and database field objects.
       
     4 """
       
     5 
       
     6 from django.utils.encoding import smart_unicode
       
     7 from django.utils.datastructures import SortedDict
       
     8 from django.utils.text import get_text_list, capfirst
       
     9 from django.utils.translation import ugettext_lazy as _
       
    10 
       
    11 from util import ValidationError, ErrorList
       
    12 from forms import BaseForm, get_declared_fields
       
    13 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
       
    14 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
       
    15 from widgets import media_property
       
    16 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
       
    17 
       
    18 try:
       
    19     set
       
    20 except NameError:
       
    21     from sets import Set as set     # Python 2.3 fallback
       
    22 
       
    23 __all__ = (
       
    24     'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
       
    25     'save_instance', 'form_for_fields', 'ModelChoiceField',
       
    26     'ModelMultipleChoiceField',
       
    27 )
       
    28 
       
    29 
       
    30 def save_instance(form, instance, fields=None, fail_message='saved',
       
    31                   commit=True, exclude=None):
       
    32     """
       
    33     Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
       
    34 
       
    35     If commit=True, then the changes to ``instance`` will be saved to the
       
    36     database. Returns ``instance``.
       
    37     """
       
    38     from django.db import models
       
    39     opts = instance._meta
       
    40     if form.errors:
       
    41         raise ValueError("The %s could not be %s because the data didn't"
       
    42                          " validate." % (opts.object_name, fail_message))
       
    43     cleaned_data = form.cleaned_data
       
    44     for f in opts.fields:
       
    45         if not f.editable or isinstance(f, models.AutoField) \
       
    46                 or not f.name in cleaned_data:
       
    47             continue
       
    48         if fields and f.name not in fields:
       
    49             continue
       
    50         if exclude and f.name in exclude:
       
    51             continue
       
    52         f.save_form_data(instance, cleaned_data[f.name])
       
    53     # Wrap up the saving of m2m data as a function.
       
    54     def save_m2m():
       
    55         opts = instance._meta
       
    56         cleaned_data = form.cleaned_data
       
    57         for f in opts.many_to_many:
       
    58             if fields and f.name not in fields:
       
    59                 continue
       
    60             if f.name in cleaned_data:
       
    61                 f.save_form_data(instance, cleaned_data[f.name])
       
    62     if commit:
       
    63         # If we are committing, save the instance and the m2m data immediately.
       
    64         instance.save()
       
    65         save_m2m()
       
    66     else:
       
    67         # We're not committing. Add a method to the form to allow deferred
       
    68         # saving of m2m data.
       
    69         form.save_m2m = save_m2m
       
    70     return instance
       
    71 
       
    72 def make_model_save(model, fields, fail_message):
       
    73     """Returns the save() method for a Form."""
       
    74     def save(self, commit=True):
       
    75         return save_instance(self, model(), fields, fail_message, commit)
       
    76     return save
       
    77 
       
    78 def make_instance_save(instance, fields, fail_message):
       
    79     """Returns the save() method for a Form."""
       
    80     def save(self, commit=True):
       
    81         return save_instance(self, instance, fields, fail_message, commit)
       
    82     return save
       
    83 
       
    84 def form_for_fields(field_list):
       
    85     """
       
    86     Returns a Form class for the given list of Django database field instances.
       
    87     """
       
    88     fields = SortedDict([(f.name, f.formfield())
       
    89                          for f in field_list if f.editable])
       
    90     return type('FormForFields', (BaseForm,), {'base_fields': fields})
       
    91 
       
    92 
       
    93 # ModelForms #################################################################
       
    94 
       
    95 def model_to_dict(instance, fields=None, exclude=None):
       
    96     """
       
    97     Returns a dict containing the data in ``instance`` suitable for passing as
       
    98     a Form's ``initial`` keyword argument.
       
    99 
       
   100     ``fields`` is an optional list of field names. If provided, only the named
       
   101     fields will be included in the returned dict.
       
   102 
       
   103     ``exclude`` is an optional list of field names. If provided, the named
       
   104     fields will be excluded from the returned dict, even if they are listed in
       
   105     the ``fields`` argument.
       
   106     """
       
   107     # avoid a circular import
       
   108     from django.db.models.fields.related import ManyToManyField, OneToOneField
       
   109     opts = instance._meta
       
   110     data = {}
       
   111     for f in opts.fields + opts.many_to_many:
       
   112         if not f.editable:
       
   113             continue
       
   114         if fields and not f.name in fields:
       
   115             continue
       
   116         if exclude and f.name in exclude:
       
   117             continue
       
   118         if isinstance(f, ManyToManyField):
       
   119             # If the object doesn't have a primry key yet, just use an empty
       
   120             # list for its m2m fields. Calling f.value_from_object will raise
       
   121             # an exception.
       
   122             if instance.pk is None:
       
   123                 data[f.name] = []
       
   124             else:
       
   125                 # MultipleChoiceWidget needs a list of pks, not object instances.
       
   126                 data[f.name] = [obj.pk for obj in f.value_from_object(instance)]
       
   127         else:
       
   128             data[f.name] = f.value_from_object(instance)
       
   129     return data
       
   130 
       
   131 def fields_for_model(model, fields=None, exclude=None, formfield_callback=lambda f: f.formfield()):
       
   132     """
       
   133     Returns a ``SortedDict`` containing form fields for the given model.
       
   134 
       
   135     ``fields`` is an optional list of field names. If provided, only the named
       
   136     fields will be included in the returned fields.
       
   137 
       
   138     ``exclude`` is an optional list of field names. If provided, the named
       
   139     fields will be excluded from the returned fields, even if they are listed
       
   140     in the ``fields`` argument.
       
   141     """
       
   142     # TODO: if fields is provided, it would be nice to return fields in that order
       
   143     field_list = []
       
   144     opts = model._meta
       
   145     for f in opts.fields + opts.many_to_many:
       
   146         if not f.editable:
       
   147             continue
       
   148         if fields and not f.name in fields:
       
   149             continue
       
   150         if exclude and f.name in exclude:
       
   151             continue
       
   152         formfield = formfield_callback(f)
       
   153         if formfield:
       
   154             field_list.append((f.name, formfield))
       
   155     return SortedDict(field_list)
       
   156 
       
   157 class ModelFormOptions(object):
       
   158     def __init__(self, options=None):
       
   159         self.model = getattr(options, 'model', None)
       
   160         self.fields = getattr(options, 'fields', None)
       
   161         self.exclude = getattr(options, 'exclude', None)
       
   162 
       
   163 
       
   164 class ModelFormMetaclass(type):
       
   165     def __new__(cls, name, bases, attrs):
       
   166         formfield_callback = attrs.pop('formfield_callback',
       
   167                 lambda f: f.formfield())
       
   168         try:
       
   169             parents = [b for b in bases if issubclass(b, ModelForm)]
       
   170         except NameError:
       
   171             # We are defining ModelForm itself.
       
   172             parents = None
       
   173         declared_fields = get_declared_fields(bases, attrs, False)
       
   174         new_class = super(ModelFormMetaclass, cls).__new__(cls, name, bases,
       
   175                 attrs)
       
   176         if not parents:
       
   177             return new_class
       
   178 
       
   179         if 'media' not in attrs:
       
   180             new_class.media = media_property(new_class)
       
   181         opts = new_class._meta = ModelFormOptions(getattr(new_class, 'Meta', None))
       
   182         if opts.model:
       
   183             # If a model is defined, extract form fields from it.
       
   184             fields = fields_for_model(opts.model, opts.fields,
       
   185                                       opts.exclude, formfield_callback)
       
   186             # Override default model fields with any custom declared ones
       
   187             # (plus, include all the other declared fields).
       
   188             fields.update(declared_fields)
       
   189         else:
       
   190             fields = declared_fields
       
   191         new_class.declared_fields = declared_fields
       
   192         new_class.base_fields = fields
       
   193         return new_class
       
   194 
       
   195 class BaseModelForm(BaseForm):
       
   196     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
       
   197                  initial=None, error_class=ErrorList, label_suffix=':',
       
   198                  empty_permitted=False, instance=None):
       
   199         opts = self._meta
       
   200         if instance is None:
       
   201             # if we didn't get an instance, instantiate a new one
       
   202             self.instance = opts.model()
       
   203             object_data = {}
       
   204         else:
       
   205             self.instance = instance
       
   206             object_data = model_to_dict(instance, opts.fields, opts.exclude)
       
   207         # if initial was provided, it should override the values from instance
       
   208         if initial is not None:
       
   209             object_data.update(initial)
       
   210         super(BaseModelForm, self).__init__(data, files, auto_id, prefix, object_data,
       
   211                                             error_class, label_suffix, empty_permitted)
       
   212     def clean(self):
       
   213         self.validate_unique()
       
   214         return self.cleaned_data
       
   215 
       
   216     def validate_unique(self):
       
   217         from django.db.models.fields import FieldDoesNotExist
       
   218 
       
   219         # Gather a list of checks to perform. Since this is a ModelForm, some
       
   220         # fields may have been excluded; we can't perform a unique check on a
       
   221         # form that is missing fields involved in that check.
       
   222         unique_checks = []
       
   223         for check in self.instance._meta.unique_together[:]:
       
   224             fields_on_form = [field for field in check if field in self.fields]
       
   225             if len(fields_on_form) == len(check):
       
   226                 unique_checks.append(check)
       
   227 
       
   228         form_errors = []
       
   229 
       
   230         # Gather a list of checks for fields declared as unique and add them to
       
   231         # the list of checks. Again, skip fields not on the form.
       
   232         for name, field in self.fields.items():
       
   233             try:
       
   234                 f = self.instance._meta.get_field_by_name(name)[0]
       
   235             except FieldDoesNotExist:
       
   236                 # This is an extra field that's not on the ModelForm, ignore it
       
   237                 continue
       
   238             # MySQL can't handle ... WHERE pk IS NULL, so make sure we
       
   239             # don't generate queries of that form.
       
   240             is_null_pk = f.primary_key and self.cleaned_data[name] is None
       
   241             if name in self.cleaned_data and f.unique and not is_null_pk:
       
   242                 unique_checks.append((name,))
       
   243 
       
   244         # Don't run unique checks on fields that already have an error.
       
   245         unique_checks = [check for check in unique_checks if not [x in self._errors for x in check if x in self._errors]]
       
   246 
       
   247         bad_fields = set()
       
   248         for unique_check in unique_checks:
       
   249             # Try to look up an existing object with the same values as this
       
   250             # object's values for all the unique field.
       
   251 
       
   252             lookup_kwargs = {}
       
   253             for field_name in unique_check:
       
   254                 lookup_kwargs[field_name] = self.cleaned_data[field_name]
       
   255 
       
   256             qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
       
   257 
       
   258             # Exclude the current object from the query if we are editing an
       
   259             # instance (as opposed to creating a new one)
       
   260             if self.instance.pk is not None:
       
   261                 qs = qs.exclude(pk=self.instance.pk)
       
   262 
       
   263             # This cute trick with extra/values is the most efficient way to
       
   264             # tell if a particular query returns any results.
       
   265             if qs.extra(select={'a': 1}).values('a').order_by():
       
   266                 model_name = capfirst(self.instance._meta.verbose_name)
       
   267 
       
   268                 # A unique field
       
   269                 if len(unique_check) == 1:
       
   270                     field_name = unique_check[0]
       
   271                     field_label = self.fields[field_name].label
       
   272                     # Insert the error into the error dict, very sneaky
       
   273                     self._errors[field_name] = ErrorList([
       
   274                         _(u"%(model_name)s with this %(field_label)s already exists.") % \
       
   275                         {'model_name': unicode(model_name),
       
   276                          'field_label': unicode(field_label)}
       
   277                     ])
       
   278                 # unique_together
       
   279                 else:
       
   280                     field_labels = [self.fields[field_name].label for field_name in unique_check]
       
   281                     field_labels = get_text_list(field_labels, _('and'))
       
   282                     form_errors.append(
       
   283                         _(u"%(model_name)s with this %(field_label)s already exists.") % \
       
   284                         {'model_name': unicode(model_name),
       
   285                          'field_label': unicode(field_labels)}
       
   286                     )
       
   287 
       
   288                 # Mark these fields as needing to be removed from cleaned data
       
   289                 # later.
       
   290                 for field_name in unique_check:
       
   291                     bad_fields.add(field_name)
       
   292 
       
   293         for field_name in bad_fields:
       
   294             del self.cleaned_data[field_name]
       
   295         if form_errors:
       
   296             # Raise the unique together errors since they are considered
       
   297             # form-wide.
       
   298             raise ValidationError(form_errors)
       
   299 
       
   300     def save(self, commit=True):
       
   301         """
       
   302         Saves this ``form``'s cleaned_data into model instance
       
   303         ``self.instance``.
       
   304 
       
   305         If commit=True, then the changes to ``instance`` will be saved to the
       
   306         database. Returns ``instance``.
       
   307         """
       
   308         if self.instance.pk is None:
       
   309             fail_message = 'created'
       
   310         else:
       
   311             fail_message = 'changed'
       
   312         return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
       
   313 
       
   314 class ModelForm(BaseModelForm):
       
   315     __metaclass__ = ModelFormMetaclass
       
   316 
       
   317 def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
       
   318                        formfield_callback=lambda f: f.formfield()):
       
   319     # HACK: we should be able to construct a ModelForm without creating
       
   320     # and passing in a temporary inner class
       
   321     class Meta:
       
   322         pass
       
   323     setattr(Meta, 'model', model)
       
   324     setattr(Meta, 'fields', fields)
       
   325     setattr(Meta, 'exclude', exclude)
       
   326     class_name = model.__name__ + 'Form'
       
   327     return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
       
   328                               'formfield_callback': formfield_callback})
       
   329 
       
   330 
       
   331 # ModelFormSets ##############################################################
       
   332 
       
   333 class BaseModelFormSet(BaseFormSet):
       
   334     """
       
   335     A ``FormSet`` for editing a queryset and/or adding new objects to it.
       
   336     """
       
   337     model = None
       
   338 
       
   339     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
       
   340                  queryset=None, **kwargs):
       
   341         self.queryset = queryset
       
   342         defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
       
   343         defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()]
       
   344         defaults.update(kwargs)
       
   345         super(BaseModelFormSet, self).__init__(**defaults)
       
   346 
       
   347     def _construct_form(self, i, **kwargs):
       
   348         if i < self._initial_form_count:
       
   349             kwargs['instance'] = self.get_queryset()[i]
       
   350         return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
       
   351 
       
   352     def get_queryset(self):
       
   353         if not hasattr(self, '_queryset'):
       
   354             if self.queryset is not None:
       
   355                 qs = self.queryset
       
   356             else:
       
   357                 qs = self.model._default_manager.get_query_set()
       
   358             if self.max_num > 0:
       
   359                 self._queryset = qs[:self.max_num]
       
   360             else:
       
   361                 self._queryset = qs
       
   362         return self._queryset
       
   363 
       
   364     def save_new(self, form, commit=True):
       
   365         """Saves and returns a new model instance for the given form."""
       
   366         return save_instance(form, self.model(), exclude=[self._pk_field.name], commit=commit)
       
   367 
       
   368     def save_existing(self, form, instance, commit=True):
       
   369         """Saves and returns an existing model instance for the given form."""
       
   370         return save_instance(form, instance, exclude=[self._pk_field.name], commit=commit)
       
   371 
       
   372     def save(self, commit=True):
       
   373         """Saves model instances for every form, adding and changing instances
       
   374         as necessary, and returns the list of instances.
       
   375         """
       
   376         if not commit:
       
   377             self.saved_forms = []
       
   378             def save_m2m():
       
   379                 for form in self.saved_forms:
       
   380                     form.save_m2m()
       
   381             self.save_m2m = save_m2m
       
   382         return self.save_existing_objects(commit) + self.save_new_objects(commit)
       
   383 
       
   384     def save_existing_objects(self, commit=True):
       
   385         self.changed_objects = []
       
   386         self.deleted_objects = []
       
   387         if not self.get_queryset():
       
   388             return []
       
   389 
       
   390         # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
       
   391         existing_objects = {}
       
   392         for obj in self.get_queryset():
       
   393             existing_objects[obj.pk] = obj
       
   394         saved_instances = []
       
   395         for form in self.initial_forms:
       
   396             obj = existing_objects[form.cleaned_data[self._pk_field.name]]
       
   397             if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
       
   398                 self.deleted_objects.append(obj)
       
   399                 obj.delete()
       
   400             else:
       
   401                 if form.changed_data:
       
   402                     self.changed_objects.append((obj, form.changed_data))
       
   403                     saved_instances.append(self.save_existing(form, obj, commit=commit))
       
   404                     if not commit:
       
   405                         self.saved_forms.append(form)
       
   406         return saved_instances
       
   407 
       
   408     def save_new_objects(self, commit=True):
       
   409         self.new_objects = []
       
   410         for form in self.extra_forms:
       
   411             if not form.has_changed():
       
   412                 continue
       
   413             # If someone has marked an add form for deletion, don't save the
       
   414             # object.
       
   415             if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
       
   416                 continue
       
   417             self.new_objects.append(self.save_new(form, commit=commit))
       
   418             if not commit:
       
   419                 self.saved_forms.append(form)
       
   420         return self.new_objects
       
   421 
       
   422     def add_fields(self, form, index):
       
   423         """Add a hidden field for the object's primary key."""
       
   424         from django.db.models import AutoField
       
   425         self._pk_field = pk = self.model._meta.pk
       
   426         if pk.auto_created or isinstance(pk, AutoField):
       
   427             form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
       
   428         super(BaseModelFormSet, self).add_fields(form, index)
       
   429 
       
   430 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
       
   431                          formset=BaseModelFormSet,
       
   432                          extra=1, can_delete=False, can_order=False,
       
   433                          max_num=0, fields=None, exclude=None):
       
   434     """
       
   435     Returns a FormSet class for the given Django model class.
       
   436     """
       
   437     form = modelform_factory(model, form=form, fields=fields, exclude=exclude,
       
   438                              formfield_callback=formfield_callback)
       
   439     FormSet = formset_factory(form, formset, extra=extra, max_num=max_num,
       
   440                               can_order=can_order, can_delete=can_delete)
       
   441     FormSet.model = model
       
   442     return FormSet
       
   443 
       
   444 
       
   445 # InlineFormSets #############################################################
       
   446 
       
   447 class BaseInlineFormSet(BaseModelFormSet):
       
   448     """A formset for child objects related to a parent."""
       
   449     def __init__(self, data=None, files=None, instance=None,
       
   450                  save_as_new=False, prefix=None):
       
   451         from django.db.models.fields.related import RelatedObject
       
   452         self.instance = instance
       
   453         self.save_as_new = save_as_new
       
   454         # is there a better way to get the object descriptor?
       
   455         self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
       
   456         super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix or self.rel_name)
       
   457 
       
   458     def _construct_forms(self):
       
   459         if self.save_as_new:
       
   460             self._total_form_count = self._initial_form_count
       
   461             self._initial_form_count = 0
       
   462         super(BaseInlineFormSet, self)._construct_forms()
       
   463 
       
   464     def _construct_form(self, i, **kwargs):
       
   465         form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
       
   466         if self.save_as_new:
       
   467             # Remove the primary key from the form's data, we are only
       
   468             # creating new instances
       
   469             form.data[form.add_prefix(self._pk_field.name)] = None
       
   470         return form
       
   471 
       
   472     def get_queryset(self):
       
   473         """
       
   474         Returns this FormSet's queryset, but restricted to children of
       
   475         self.instance
       
   476         """
       
   477         kwargs = {self.fk.name: self.instance}
       
   478         return self.model._default_manager.filter(**kwargs)
       
   479 
       
   480     def save_new(self, form, commit=True):
       
   481         kwargs = {self.fk.get_attname(): self.instance.pk}
       
   482         new_obj = self.model(**kwargs)
       
   483         return save_instance(form, new_obj, exclude=[self._pk_field.name], commit=commit)
       
   484 
       
   485     def add_fields(self, form, index):
       
   486         super(BaseInlineFormSet, self).add_fields(form, index)
       
   487         if self._pk_field == self.fk:
       
   488             form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
       
   489 
       
   490 def _get_foreign_key(parent_model, model, fk_name=None):
       
   491     """
       
   492     Finds and returns the ForeignKey from model to parent if there is one.
       
   493     If fk_name is provided, assume it is the name of the ForeignKey field.
       
   494     """
       
   495     # avoid circular import
       
   496     from django.db.models import ForeignKey
       
   497     opts = model._meta
       
   498     if fk_name:
       
   499         fks_to_parent = [f for f in opts.fields if f.name == fk_name]
       
   500         if len(fks_to_parent) == 1:
       
   501             fk = fks_to_parent[0]
       
   502             if not isinstance(fk, ForeignKey) or \
       
   503                     (fk.rel.to != parent_model and
       
   504                      fk.rel.to not in parent_model._meta.get_parent_list()):
       
   505                 raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
       
   506         elif len(fks_to_parent) == 0:
       
   507             raise Exception("%s has no field named '%s'" % (model, fk_name))
       
   508     else:
       
   509         # Try to discover what the ForeignKey from model to parent_model is
       
   510         fks_to_parent = [
       
   511             f for f in opts.fields
       
   512             if isinstance(f, ForeignKey)
       
   513             and (f.rel.to == parent_model
       
   514                 or f.rel.to in parent_model._meta.get_parent_list())
       
   515         ]
       
   516         if len(fks_to_parent) == 1:
       
   517             fk = fks_to_parent[0]
       
   518         elif len(fks_to_parent) == 0:
       
   519             raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
       
   520         else:
       
   521             raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
       
   522     return fk
       
   523 
       
   524 
       
   525 def inlineformset_factory(parent_model, model, form=ModelForm,
       
   526                           formset=BaseInlineFormSet, fk_name=None,
       
   527                           fields=None, exclude=None,
       
   528                           extra=3, can_order=False, can_delete=True, max_num=0,
       
   529                           formfield_callback=lambda f: f.formfield()):
       
   530     """
       
   531     Returns an ``InlineFormSet`` for the given kwargs.
       
   532 
       
   533     You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
       
   534     to ``parent_model``.
       
   535     """
       
   536     fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
       
   537     # enforce a max_num=1 when the foreign key to the parent model is unique.
       
   538     if fk.unique:
       
   539         max_num = 1
       
   540     if exclude is not None:
       
   541         exclude = list(exclude)
       
   542         exclude.append(fk.name)
       
   543     else:
       
   544         exclude = [fk.name]
       
   545     kwargs = {
       
   546         'form': form,
       
   547         'formfield_callback': formfield_callback,
       
   548         'formset': formset,
       
   549         'extra': extra,
       
   550         'can_delete': can_delete,
       
   551         'can_order': can_order,
       
   552         'fields': fields,
       
   553         'exclude': exclude,
       
   554         'max_num': max_num,
       
   555     }
       
   556     FormSet = modelformset_factory(model, **kwargs)
       
   557     FormSet.fk = fk
       
   558     return FormSet
       
   559 
       
   560 
       
   561 # Fields #####################################################################
       
   562 
       
   563 class ModelChoiceIterator(object):
       
   564     def __init__(self, field):
       
   565         self.field = field
       
   566         self.queryset = field.queryset
       
   567 
       
   568     def __iter__(self):
       
   569         if self.field.empty_label is not None:
       
   570             yield (u"", self.field.empty_label)
       
   571         if self.field.cache_choices:
       
   572             if self.field.choice_cache is None:
       
   573                 self.field.choice_cache = [
       
   574                     self.choice(obj) for obj in self.queryset.all()
       
   575                 ]
       
   576             for choice in self.field.choice_cache:
       
   577                 yield choice
       
   578         else:
       
   579             for obj in self.queryset.all():
       
   580                 yield self.choice(obj)
       
   581 
       
   582     def choice(self, obj):
       
   583         if self.field.to_field_name:
       
   584             # FIXME: The try..except shouldn't be necessary here. But this is
       
   585             # going in just before 1.0, so I want to be careful. Will check it
       
   586             # out later.
       
   587             try:
       
   588                 key = getattr(obj, self.field.to_field_name).pk
       
   589             except AttributeError:
       
   590                 key = getattr(obj, self.field.to_field_name)
       
   591         else:
       
   592             key = obj.pk
       
   593         return (key, self.field.label_from_instance(obj))
       
   594 
       
   595 
       
   596 class ModelChoiceField(ChoiceField):
       
   597     """A ChoiceField whose choices are a model QuerySet."""
       
   598     # This class is a subclass of ChoiceField for purity, but it doesn't
       
   599     # actually use any of ChoiceField's implementation.
       
   600     default_error_messages = {
       
   601         'invalid_choice': _(u'Select a valid choice. That choice is not one of'
       
   602                             u' the available choices.'),
       
   603     }
       
   604 
       
   605     def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
       
   606                  required=True, widget=None, label=None, initial=None,
       
   607                  help_text=None, to_field_name=None, *args, **kwargs):
       
   608         self.empty_label = empty_label
       
   609         self.cache_choices = cache_choices
       
   610 
       
   611         # Call Field instead of ChoiceField __init__() because we don't need
       
   612         # ChoiceField.__init__().
       
   613         Field.__init__(self, required, widget, label, initial, help_text,
       
   614                        *args, **kwargs)
       
   615         self.queryset = queryset
       
   616         self.choice_cache = None
       
   617         self.to_field_name = to_field_name
       
   618 
       
   619     def _get_queryset(self):
       
   620         return self._queryset
       
   621 
       
   622     def _set_queryset(self, queryset):
       
   623         self._queryset = queryset
       
   624         self.widget.choices = self.choices
       
   625 
       
   626     queryset = property(_get_queryset, _set_queryset)
       
   627 
       
   628     # this method will be used to create object labels by the QuerySetIterator.
       
   629     # Override it to customize the label.
       
   630     def label_from_instance(self, obj):
       
   631         """
       
   632         This method is used to convert objects into strings; it's used to
       
   633         generate the labels for the choices presented by this object. Subclasses
       
   634         can override this method to customize the display of the choices.
       
   635         """
       
   636         return smart_unicode(obj)
       
   637 
       
   638     def _get_choices(self):
       
   639         # If self._choices is set, then somebody must have manually set
       
   640         # the property self.choices. In this case, just return self._choices.
       
   641         if hasattr(self, '_choices'):
       
   642             return self._choices
       
   643 
       
   644         # Otherwise, execute the QuerySet in self.queryset to determine the
       
   645         # choices dynamically. Return a fresh QuerySetIterator that has not been
       
   646         # consumed. Note that we're instantiating a new QuerySetIterator *each*
       
   647         # time _get_choices() is called (and, thus, each time self.choices is
       
   648         # accessed) so that we can ensure the QuerySet has not been consumed. This
       
   649         # construct might look complicated but it allows for lazy evaluation of
       
   650         # the queryset.
       
   651         return ModelChoiceIterator(self)
       
   652 
       
   653     choices = property(_get_choices, ChoiceField._set_choices)
       
   654 
       
   655     def clean(self, value):
       
   656         Field.clean(self, value)
       
   657         if value in EMPTY_VALUES:
       
   658             return None
       
   659         try:
       
   660             key = self.to_field_name or 'pk'
       
   661             value = self.queryset.get(**{key: value})
       
   662         except self.queryset.model.DoesNotExist:
       
   663             raise ValidationError(self.error_messages['invalid_choice'])
       
   664         return value
       
   665 
       
   666 class ModelMultipleChoiceField(ModelChoiceField):
       
   667     """A MultipleChoiceField whose choices are a model QuerySet."""
       
   668     widget = SelectMultiple
       
   669     hidden_widget = MultipleHiddenInput
       
   670     default_error_messages = {
       
   671         'list': _(u'Enter a list of values.'),
       
   672         'invalid_choice': _(u'Select a valid choice. %s is not one of the'
       
   673                             u' available choices.'),
       
   674     }
       
   675 
       
   676     def __init__(self, queryset, cache_choices=False, required=True,
       
   677                  widget=None, label=None, initial=None,
       
   678                  help_text=None, *args, **kwargs):
       
   679         super(ModelMultipleChoiceField, self).__init__(queryset, None,
       
   680             cache_choices, required, widget, label, initial, help_text,
       
   681             *args, **kwargs)
       
   682 
       
   683     def clean(self, value):
       
   684         if self.required and not value:
       
   685             raise ValidationError(self.error_messages['required'])
       
   686         elif not self.required and not value:
       
   687             return []
       
   688         if not isinstance(value, (list, tuple)):
       
   689             raise ValidationError(self.error_messages['list'])
       
   690         final_values = []
       
   691         for val in value:
       
   692             try:
       
   693                 obj = self.queryset.get(pk=val)
       
   694             except self.queryset.model.DoesNotExist:
       
   695                 raise ValidationError(self.error_messages['invalid_choice'] % val)
       
   696             else:
       
   697                 final_values.append(obj)
       
   698         return final_values