Code

Ticket #6241: formset_refactor_8.diff

File formset_refactor_8.diff, 47.9 KB (added by Øyvind Saltvik <oyvind@…>, 6 years ago)

Slight change to BaseModelFormSet to allow queryset as a kwarg

Line 
1Index: django/newforms/options.py
2===================================================================
3--- django/newforms/options.py  (revision 0)
4+++ django/newforms/options.py  (revision 0)
5@@ -0,0 +1,50 @@
6+
7+from forms import BaseForm
8+
9+class BaseFormOptions(object):
10+    """
11+    The base class for all options that are associated to a form object.
12+    """
13+    def __init__(self, options=None):
14+        self.fields = self._dynamic_attribute(options, "fields")
15+        self.exclude = self._dynamic_attribute(options, "exclude")
16+   
17+    def _dynamic_attribute(self, obj, key, default=None):
18+        try:
19+            return getattr(obj, key)
20+        except AttributeError:
21+            try:
22+                return obj[key]
23+            except (TypeError, KeyError):
24+                # key doesnt exist in obj or obj is None
25+                return default
26+
27+class ModelFormOptions(BaseFormOptions):
28+    """
29+    Encapsulates the options on a ModelForm class.
30+    """
31+    def __init__(self, options=None):
32+        self.model = self._dynamic_attribute(options, "model")
33+        super(ModelFormOptions, self).__init__(options)
34+
35+class FormSetOptions(BaseFormOptions):
36+    """
37+    Encapsulates the options on a FormSet class.
38+    """
39+    def __init__(self, options=None):
40+        self.form = self._dynamic_attribute(options, "form", BaseForm)
41+        self.num_extra = self._dynamic_attribute(options, "num_extra", 1)
42+        self.orderable = self._dynamic_attribute(options, "orderable", False)
43+        self.deletable = self._dynamic_attribute(options, "deletable", False)
44+        super(FormSetOptions, self).__init__(options)
45+
46+class ModelFormSetOptions(FormSetOptions, ModelFormOptions):
47+    pass
48+
49+class InlineFormSetOptions(ModelFormSetOptions):
50+    def __init__(self, options=None):
51+        super(InlineFormSetOptions, self).__init__(options)
52+        self.parent_model = self._dynamic_attribute(options, "parent_model")
53+        self.fk_name = self._dynamic_attribute(options, "fk_name")
54+        self.deletable = self._dynamic_attribute(options, "deletable", True)
55+   
56\ No newline at end of file
57Index: django/newforms/formsets.py
58===================================================================
59--- django/newforms/formsets.py (revision 6962)
60+++ django/newforms/formsets.py (working copy)
61@@ -1,9 +1,16 @@
62-from forms import Form
63-from fields import IntegerField, BooleanField
64+
65+from warnings import warn
66+
67+from django.utils.datastructures import SortedDict
68+from django.utils.translation import ugettext_lazy as _
69+
70+from forms import BaseForm, Form
71+from fields import Field, IntegerField, BooleanField
72+from options import FormSetOptions
73 from widgets import HiddenInput, Media
74 from util import ErrorList, ValidationError
75 
76-__all__ = ('BaseFormSet', 'formset_for_form', 'all_valid')
77+__all__ = ('BaseFormSet', 'FormSet', 'formset_for_form', 'all_valid')
78 
79 # special field names
80 FORM_COUNT_FIELD_NAME = 'COUNT'
81@@ -20,6 +27,24 @@
82         self.base_fields[FORM_COUNT_FIELD_NAME] = IntegerField(widget=HiddenInput)
83         super(ManagementForm, self).__init__(*args, **kwargs)
84 
85+class BaseFormSetMetaclass(type):
86+    def __new__(cls, name, bases, attrs, **options):
87+        fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
88+        fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
89+       
90+        # If this class is subclassing another FormSet, ad that FormSet's fields.
91+        # Note that we loop over the bases in *reverse*. This is necessary in
92+        # order to preserve the correct order of fields.
93+        for base in bases[::-1]:
94+            if hasattr(base, "base_fields"):
95+                fields = base.base_fields.items() + fields
96+        attrs["base_fields"] = SortedDict(fields)
97+       
98+        opts = FormSetOptions(options and options or attrs.get("Meta", None))
99+        attrs["_meta"] = opts
100+       
101+        return type.__new__(cls, name, bases, attrs)
102+
103 class BaseFormSet(object):
104     """A collection of instances of the same Form class."""
105 
106@@ -37,25 +62,24 @@
107             self.management_form = ManagementForm(data, files, auto_id=self.auto_id, prefix=self.prefix)
108             if self.management_form.is_valid():
109                 self.total_forms = self.management_form.cleaned_data[FORM_COUNT_FIELD_NAME]
110-                self.required_forms = self.total_forms - self.num_extra
111-                self.change_form_count = self.total_forms - self.num_extra
112+                self.required_forms = self.total_forms - self._meta.num_extra
113+                self.change_form_count = self.total_forms - self._meta.num_extra
114             else:
115                 # not sure that ValidationError is the best thing to raise here
116                 raise ValidationError('ManagementForm data is missing or has been tampered with')
117         elif initial:
118             self.change_form_count = len(initial)
119             self.required_forms = len(initial)
120-            self.total_forms = self.required_forms + self.num_extra
121+            self.total_forms = self.required_forms + self._meta.num_extra
122             self.management_form = ManagementForm(initial={FORM_COUNT_FIELD_NAME: self.total_forms}, auto_id=self.auto_id, prefix=self.prefix)
123         else:
124             self.change_form_count = 0
125             self.required_forms = 0
126-            self.total_forms = self.num_extra
127+            self.total_forms = self._meta.num_extra
128             self.management_form = ManagementForm(initial={FORM_COUNT_FIELD_NAME: self.total_forms}, auto_id=self.auto_id, prefix=self.prefix)
129 
130     def _get_add_forms(self):
131         """Return a list of all the add forms in this ``FormSet``."""
132-        FormClass = self.form_class
133         if not hasattr(self, '_add_forms'):
134             add_forms = []
135             for i in range(self.change_form_count, self.total_forms):
136@@ -64,7 +88,7 @@
137                     kwargs['data'] = self.data
138                 if self.files:
139                     kwargs['files'] = self.files
140-                add_form = FormClass(**kwargs)
141+                add_form = self.get_form_class(i)(**kwargs)
142                 self.add_fields(add_form, i)
143                 add_forms.append(add_form)
144             self._add_forms = add_forms
145@@ -73,7 +97,6 @@
146 
147     def _get_change_forms(self):
148         """Return a list of all the change forms in this ``FormSet``."""
149-        FormClass = self.form_class
150         if not hasattr(self, '_change_forms'):
151             change_forms = []
152             for i in range(0, self.change_form_count):
153@@ -84,10 +107,10 @@
154                     kwargs['files'] = self.files
155                 if self.initial:
156                     kwargs['initial'] = self.initial[i]
157-                change_form = FormClass(**kwargs)
158+                change_form = self.get_form_class(i)(**kwargs)
159                 self.add_fields(change_form, i)
160                 change_forms.append(change_form)
161-            self._change_forms= change_forms
162+            self._change_forms = change_forms
163         return self._change_forms
164     change_forms = property(_get_change_forms)
165 
166@@ -117,7 +140,7 @@
167         # Process change forms
168         for form in self.change_forms:
169             if form.is_valid():
170-                if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
171+                if self._meta.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
172                     self.deleted_data.append(form.cleaned_data)
173                 else:
174                     self.cleaned_data.append(form.cleaned_data)
175@@ -144,7 +167,7 @@
176         add_errors.reverse()
177         errors.extend(add_errors)
178         # Sort cleaned_data if the formset is orderable.
179-        if self.orderable:
180+        if self._meta.orderable:
181             self.cleaned_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME])
182         # Give self.clean() a chance to do validation
183         try:
184@@ -167,12 +190,20 @@
185         via formset.non_form_errors()
186         """
187         return self.cleaned_data
188+   
189+    def get_form_class(self, index):
190+        """
191+        A hook to change a form class object.
192+        """
193+        FormClass = self._meta.form
194+        FormClass.base_fields = self.base_fields
195+        return FormClass
196 
197     def add_fields(self, form, index):
198         """A hook for adding extra fields on to each form instance."""
199-        if self.orderable:
200+        if self._meta.orderable:
201             form.fields[ORDERING_FIELD_NAME] = IntegerField(label='Order', initial=index+1)
202-        if self.deletable:
203+        if self._meta.deletable:
204             form.fields[DELETION_FIELD_NAME] = BooleanField(label='Delete', required=False)
205 
206     def add_prefix(self, index):
207@@ -192,12 +223,21 @@
208         else:
209             return Media()
210     media = property(_get_media)
211+
212+class FormSet(BaseFormSet):
213+    __metaclass__ = BaseFormSetMetaclass
214     
215-def formset_for_form(form, formset=BaseFormSet, num_extra=1, orderable=False, deletable=False):
216+def formset_for_form(form, formset=FormSet, num_extra=1, orderable=False,
217+                     deletable=False):
218     """Return a FormSet for the given form class."""
219-    attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable}
220-    return type(form.__name__ + 'FormSet', (formset,), attrs)
221-
222+    warn("formset_for_form is deprecated, use FormSet instead.",
223+         PendingDeprecationWarning,
224+         stacklevel=3)
225+    return BaseFormSetMetaclass(
226+        form.__name__ + "FormSet", (formset,), form.base_fields,
227+        form=form, num_extra=num_extra, orderable=orderable,
228+        deletable=deletable)
229+       
230 def all_valid(formsets):
231     """Returns true if every formset in formsets is valid."""
232     valid = True
233Index: django/newforms/models.py
234===================================================================
235--- django/newforms/models.py   (revision 6962)
236+++ django/newforms/models.py   (working copy)
237@@ -13,12 +13,14 @@
238 from util import ValidationError, ErrorList
239 from forms import BaseForm
240 from fields import Field, ChoiceField, IntegerField, EMPTY_VALUES
241-from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME
242+from formsets import FormSetOptions, BaseFormSet, formset_for_form, DELETION_FIELD_NAME
243+from options import ModelFormOptions, ModelFormSetOptions, InlineFormSetOptions
244 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
245 
246 __all__ = (
247     'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
248     'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
249+    'ModelFormSet', 'InlineFormset', 'modelform_for_model',
250     'formset_for_model', 'inline_formset',
251     'ModelChoiceField', 'ModelMultipleChoiceField',
252 )
253@@ -207,15 +209,11 @@
254             field_list.append((f.name, formfield))
255     return SortedDict(field_list)
256 
257-class ModelFormOptions(object):
258-    def __init__(self, options=None):
259-        self.model = getattr(options, 'model', None)
260-        self.fields = getattr(options, 'fields', None)
261-        self.exclude = getattr(options, 'exclude', None)
262-
263 class ModelFormMetaclass(type):
264+    opts_class = ModelFormOptions
265+   
266     def __new__(cls, name, bases, attrs,
267-                formfield_callback=lambda f: f.formfield()):
268+                formfield_callback=lambda f: f.formfield(), **options):
269         fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
270         fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
271 
272@@ -227,7 +225,7 @@
273                 fields = base.base_fields.items() + fields
274         declared_fields = SortedDict(fields)
275 
276-        opts = ModelFormOptions(attrs.get('Meta', None))
277+        opts = cls.opts_class(options and options or attrs.get('Meta', None))
278         attrs['_meta'] = opts
279 
280         # Don't allow more than one Meta model defenition in bases. The fields
281@@ -260,7 +258,7 @@
282         else:
283             attrs['base_fields'] = declared_fields
284         return type.__new__(cls, name, bases, attrs)
285-
286+       
287 class BaseModelForm(BaseForm):
288     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
289                  initial=None, error_class=ErrorList, label_suffix=':', instance=None):
290@@ -293,7 +291,15 @@
291 class ModelForm(BaseModelForm):
292     __metaclass__ = ModelFormMetaclass
293 
294+# this should really be named form_for_model.
295+def modelform_for_model(model, form=ModelForm,
296+                        formfield_callback=lambda f: f.formfield(), **options):
297+    opts = model._meta
298+    options.update({"model": model})
299+    return ModelFormMetaclass(opts.object_name + "ModelForm", (form,),
300+                              {}, formfield_callback, **options)
301 
302+
303 # Fields #####################################################################
304 
305 class QuerySetIterator(object):
306@@ -407,47 +413,33 @@
307 
308 # Model-FormSet integration ###################################################
309 
310-def initial_data(instance, fields=None):
311-    """
312-    Return a dictionary from data in ``instance`` that is suitable for
313-    use as a ``Form`` constructor's ``initial`` argument.
314+class ModelFormSetMetaclass(ModelFormMetaclass):
315+    opts_class = ModelFormSetOptions
316 
317-    Provide ``fields`` to specify the names of specific fields to return.
318-    All field values in the instance will be returned if ``fields`` is not
319-    provided.
320-    """
321-    # avoid a circular import
322-    from django.db.models.fields.related import ManyToManyField
323-    opts = instance._meta
324-    initial = {}
325-    for f in opts.fields + opts.many_to_many:
326-        if not f.editable:
327-            continue
328-        if fields and not f.name in fields:
329-            continue
330-        if isinstance(f, ManyToManyField):
331-            # MultipleChoiceWidget needs a list of ints, not object instances.
332-            initial[f.name] = [obj.pk for obj in f.value_from_object(instance)]
333-        else:
334-            initial[f.name] = f.value_from_object(instance)
335-    return initial
336-
337 class BaseModelFormSet(BaseFormSet):
338     """
339     A ``FormSet`` for editing a queryset and/or adding new objects to it.
340     """
341-    model = None
342-    queryset = None
343 
344-    def __init__(self, qs, data=None, files=None, auto_id='id_%s', prefix=None):
345+    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, queryset=None):
346         kwargs = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
347-        self.queryset = qs
348-        kwargs['initial'] = [initial_data(obj) for obj in qs]
349+        opts = self._meta
350+        self.queryset = queryset or self.get_queryset(**kwargs)
351+        initial_data = []
352+        for obj in self.queryset:
353+            initial_data.append(model_to_dict(obj, opts.fields, opts.exclude))
354+        kwargs['initial'] = initial_data
355         super(BaseModelFormSet, self).__init__(**kwargs)
356+   
357+    def get_queryset(self, **kwargs):
358+        """
359+        Hook to returning a queryset for this model.
360+        """
361+        return self._meta.model._default_manager.all()
362 
363     def save_new(self, form, commit=True):
364         """Saves and returns a new model instance for the given form."""
365-        return save_instance(form, self.model(), commit=commit)
366+        return save_instance(form, self._meta.model(), commit=commit)
367 
368     def save_instance(self, form, instance, commit=True):
369         """Saves and returns an existing model instance for the given form."""
370@@ -464,12 +456,13 @@
371             return []
372         # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
373         existing_objects = {}
374+        opts = self._meta
375         for obj in self.queryset:
376             existing_objects[obj.pk] = obj
377         saved_instances = []
378         for form in self.change_forms:
379-            obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]]
380-            if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
381+            obj = existing_objects[form.cleaned_data[opts.model._meta.pk.attname]]
382+            if opts.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
383                 obj.delete()
384             else:
385                 saved_instances.append(self.save_instance(form, obj, commit=commit))
386@@ -477,25 +470,29 @@
387 
388     def save_new_objects(self, commit=True):
389         new_objects = []
390+        opts = self._meta
391         for form in self.add_forms:
392             if form.is_empty():
393                 continue
394             # If someone has marked an add form for deletion, don't save the
395             # object. At some point it would be nice if we didn't display
396             # the deletion widget for add forms.
397-            if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
398+            if opts.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
399                 continue
400             new_objects.append(self.save_new(form, commit=commit))
401         return new_objects
402 
403     def add_fields(self, form, index):
404         """Add a hidden field for the object's primary key."""
405-        self._pk_field_name = self.model._meta.pk.attname
406+        self._pk_field_name = self._meta.model._meta.pk.attname
407         form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput)
408         super(BaseModelFormSet, self).add_fields(form, index)
409 
410-def formset_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield(),
411-                      formset=BaseModelFormSet, extra=1, orderable=False, deletable=False, fields=None):
412+class ModelFormSet(BaseModelFormSet):
413+    __metaclass__ = ModelFormSetMetaclass
414+
415+def formset_for_model(model, formset=BaseModelFormSet,
416+                      formfield_callback=lambda f: f.formfield(), **options):
417     """
418     Returns a FormSet class for the given Django model class. This FormSet
419     will contain change forms for every instance of the given model as well
420@@ -504,80 +501,101 @@
421     This is essentially the same as ``formset_for_queryset``, but automatically
422     uses the model's default manager to determine the queryset.
423     """
424-    form = form_for_model(model, form=form, fields=fields, formfield_callback=formfield_callback)
425-    FormSet = formset_for_form(form, formset, extra, orderable, deletable)
426-    FormSet.model = model
427-    return FormSet
428+    opts = model._meta
429+    options.update({"model": model})
430+    return ModelFormSetMetaclass(opts.object_name + "ModelFormSet", (formset,),
431+                                 {}, **options)
432 
433-class InlineFormset(BaseModelFormSet):
434+class InlineFormSetMetaclass(ModelFormSetMetaclass):
435+    opts_class = InlineFormSetOptions
436+   
437+    def __new__(cls, name, bases, attrs,
438+                formfield_callback=lambda f: f.formfield(), **options):
439+        formset = super(InlineFormSetMetaclass, cls).__new__(cls, name, bases, attrs,
440+            formfield_callback, **options)
441+        # If this isn't a subclass of InlineFormset, don't do anything special.
442+        try:
443+            if not filter(lambda b: issubclass(b, InlineFormset), bases):
444+                return formset
445+        except NameError:
446+            # 'InlineFormset' isn't defined yet, meaning we're looking at
447+            # Django's own InlineFormset class, defined below.
448+            return formset
449+        opts = formset._meta
450+        # resolve the foreign key
451+        fk = cls.resolve_foreign_key(opts.parent_model, opts.model, opts.fk_name)
452+        # remove the fk from base_fields to keep it transparent to the form.
453+        try:
454+            del formset.base_fields[fk.name]
455+        except KeyError:
456+            pass
457+        formset.fk = fk
458+        return formset
459+   
460+    def _resolve_foreign_key(cls, parent_model, model, fk_name=None):
461+        """
462+        Finds and returns the ForeignKey from model to parent if there is one.
463+        If fk_name is provided, assume it is the name of the ForeignKey field.
464+        """
465+        # avoid a circular import
466+        from django.db.models import ForeignKey
467+        opts = model._meta
468+        if fk_name:
469+            fks_to_parent = [f for f in opts.fields if f.name == fk_name]
470+            if len(fks_to_parent) == 1:
471+                fk = fks_to_parent[0]
472+                if not isinstance(fk, ForeignKey) or fk.rel.to != parent_model:
473+                    raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
474+            elif len(fks_to_parent) == 0:
475+                raise Exception("%s has no field named '%s'" % (model, fk_name))
476+        else:
477+            # Try to discover what the ForeignKey from model to parent_model is
478+            fks_to_parent = [f for f in opts.fields if isinstance(f, ForeignKey) and f.rel.to == parent_model]
479+            if len(fks_to_parent) == 1:
480+                fk = fks_to_parent[0]
481+            elif len(fks_to_parent) == 0:
482+                raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
483+            else:
484+                raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
485+        return fk
486+    resolve_foreign_key = classmethod(_resolve_foreign_key)
487+
488+class BaseInlineFormSet(BaseModelFormSet):
489     """A formset for child objects related to a parent."""
490-    def __init__(self, instance, data=None, files=None):
491+    def __init__(self, *args, **kwargs):
492         from django.db.models.fields.related import RelatedObject
493-        self.instance = instance
494+        opts = self._meta
495+        self.instance = kwargs.pop("instance", None)
496         # is there a better way to get the object descriptor?
497-        self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
498-        qs = self.get_queryset()
499-        super(InlineFormset, self).__init__(qs, data, files, prefix=self.rel_name)
500+        rel_name = RelatedObject(self.fk.rel.to, opts.model, self.fk).get_accessor_name()
501+        kwargs["prefix"] = rel_name
502+        super(BaseInlineFormSet, self).__init__(*args, **kwargs)
503 
504-    def get_queryset(self):
505+    def get_queryset(self, **kwargs):
506         """
507         Returns this FormSet's queryset, but restricted to children of
508         self.instance
509         """
510-        kwargs = {self.fk.name: self.instance}
511-        return self.model._default_manager.filter(**kwargs)
512+        queryset = super(BaseInlineFormSet, self).get_queryset(**kwargs)
513+        return queryset.filter(**{self.fk.name: self.instance})
514 
515     def save_new(self, form, commit=True):
516         kwargs = {self.fk.get_attname(): self.instance.pk}
517-        new_obj = self.model(**kwargs)
518+        new_obj = self._meta.model(**kwargs)
519         return save_instance(form, new_obj, commit=commit)
520 
521-def get_foreign_key(parent_model, model, fk_name=None):
522-    """
523-    Finds and returns the ForeignKey from model to parent if there is one.
524-    If fk_name is provided, assume it is the name of the ForeignKey field.
525-    """
526-    # avoid circular import
527-    from django.db.models import ForeignKey
528-    opts = model._meta
529-    if fk_name:
530-        fks_to_parent = [f for f in opts.fields if f.name == fk_name]
531-        if len(fks_to_parent) == 1:
532-            fk = fks_to_parent[0]
533-            if not isinstance(fk, ForeignKey) or fk.rel.to != parent_model:
534-                raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
535-        elif len(fks_to_parent) == 0:
536-            raise Exception("%s has no field named '%s'" % (model, fk_name))
537-    else:
538-        # Try to discover what the ForeignKey from model to parent_model is
539-        fks_to_parent = [f for f in opts.fields if isinstance(f, ForeignKey) and f.rel.to == parent_model]
540-        if len(fks_to_parent) == 1:
541-            fk = fks_to_parent[0]
542-        elif len(fks_to_parent) == 0:
543-            raise Exception("%s has no ForeignKey to %s" % (model, parent_model))
544-        else:
545-            raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model))
546-    return fk
547+class InlineFormset(BaseInlineFormSet):
548+    __metaclass__ = InlineFormSetMetaclass
549 
550-def inline_formset(parent_model, model, fk_name=None, fields=None, extra=3, orderable=False, deletable=True, formfield_callback=lambda f: f.formfield()):
551+def inline_formset(parent_model, model, formset=InlineFormset,
552+                   formfield_callback=lambda f: f.formfield(), **options):
553     """
554     Returns an ``InlineFormset`` for the given kwargs.
555 
556     You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
557     to ``parent_model``.
558     """
559-    fk = get_foreign_key(parent_model, model, fk_name=fk_name)
560-    # let the formset handle object deletion by default
561-    FormSet = formset_for_model(model, formset=InlineFormset, fields=fields,
562-                                formfield_callback=formfield_callback,
563-                                extra=extra, orderable=orderable,
564-                                deletable=deletable)
565-    # HACK: remove the ForeignKey to the parent from every form
566-    # This should be done a line above before we pass 'fields' to formset_for_model
567-    # an 'omit' argument would be very handy here
568-    try:
569-        del FormSet.form_class.base_fields[fk.name]
570-    except KeyError:
571-        pass
572-    FormSet.fk = fk
573-    return FormSet
574+    opts = model._meta
575+    options.update({"parent_model": parent_model, "model": model})
576+    return InlineFormSetMetaclass(opts.object_name + "InlineFormset", (formset,),
577+                                  {}, formfield_callback, **options)
578Index: django/contrib/admin/options.py
579===================================================================
580--- django/contrib/admin/options.py     (revision 6962)
581+++ django/contrib/admin/options.py     (working copy)
582@@ -342,7 +342,7 @@
583             fields = flatten_fieldsets(self.declared_fieldsets)
584         else:
585             fields = None
586-        return forms.form_for_model(self.model, fields=fields, formfield_callback=self.formfield_for_dbfield)
587+        return forms.modelform_for_model(self.model, fields=fields, formfield_callback=self.formfield_for_dbfield)
588 
589     def form_change(self, request, obj):
590         """
591@@ -352,7 +352,7 @@
592             fields = flatten_fieldsets(self.declared_fieldsets)
593         else:
594             fields = None
595-        return forms.form_for_instance(obj, fields=fields, formfield_callback=self.formfield_for_dbfield)
596+        return forms.modelform_for_model(self.model, fields=fields, formfield_callback=self.formfield_for_dbfield)
597 
598     def save_add(self, request, model, form, formsets, post_url_continue):
599         """
600@@ -492,20 +492,20 @@
601             # Object list will give 'Permission Denied', so go back to admin home
602             post_url = '../../../'
603 
604-        ModelForm = self.form_add(request)
605+        Form = self.form_add(request)
606         inline_formsets = []
607         obj = self.model()
608         if request.method == 'POST':
609-            form = ModelForm(request.POST, request.FILES)
610+            form = Form(request.POST, request.FILES)
611             for FormSet in self.formsets_add(request):
612-                inline_formset = FormSet(obj, data=request.POST, files=request.FILES)
613+                inline_formset = FormSet(request.POST, request.FILES)
614                 inline_formsets.append(inline_formset)
615             if all_valid(inline_formsets) and form.is_valid():
616                 return self.save_add(request, model, form, inline_formsets, '../%s/')
617         else:
618-            form = ModelForm(initial=request.GET)
619+            form = Form(initial=request.GET)
620             for FormSet in self.formsets_add(request):
621-                inline_formset = FormSet(obj)
622+                inline_formset = FormSet()
623                 inline_formsets.append(inline_formset)
624 
625         adminForm = AdminForm(form, list(self.fieldsets_add(request)), self.prepopulated_fields)
626@@ -552,20 +552,20 @@
627         if request.POST and request.POST.has_key("_saveasnew"):
628             return self.add_view(request, form_url='../../add/')
629 
630-        ModelForm = self.form_change(request, obj)
631+        Form = self.form_change(request, obj)
632         inline_formsets = []
633         if request.method == 'POST':
634-            form = ModelForm(request.POST, request.FILES)
635+            form = Form(request.POST, request.FILES, instance=obj)
636             for FormSet in self.formsets_change(request, obj):
637-                inline_formset = FormSet(obj, request.POST, request.FILES)
638+                inline_formset = FormSet(request.POST, request.FILES, instance=obj)
639                 inline_formsets.append(inline_formset)
640 
641             if all_valid(inline_formsets) and form.is_valid():
642                 return self.save_change(request, model, form, inline_formsets)
643         else:
644-            form = ModelForm()
645+            form = Form(instance=obj, initial=request.GET)
646             for FormSet in self.formsets_change(request, obj):
647-                inline_formset = FormSet(obj)
648+                inline_formset = FormSet(instance=obj)
649                 inline_formsets.append(inline_formset)
650 
651         ## Populate the FormWrapper.
652@@ -755,14 +755,14 @@
653     def fieldsets_add(self, request):
654         if self.declared_fieldsets:
655             return self.declared_fieldsets
656-        form = self.formset_add(request).form_class
657-        return [(None, {'fields': form.base_fields.keys()})]
658+        formset = self.formset_add(request)
659+        return [(None, {'fields': formset.base_fields.keys()})]
660 
661     def fieldsets_change(self, request, obj):
662         if self.declared_fieldsets:
663             return self.declared_fieldsets
664-        form = self.formset_change(request, obj).form_class
665-        return [(None, {'fields': form.base_fields.keys()})]
666+        formset = self.formset_change(request, obj)
667+        return [(None, {'fields': formset.base_fields.keys()})]
668 
669 class StackedInline(InlineModelAdmin):
670     template = 'admin/edit_inline/stacked.html'
671@@ -778,6 +778,10 @@
672         self.opts = inline
673         self.formset = formset
674         self.fieldsets = fieldsets
675+        # place orderable and deletable here since _meta is inaccesible in the
676+        # templates.
677+        self.orderable = formset._meta.orderable
678+        self.deletable = formset._meta.deletable
679 
680     def __iter__(self):
681         for form, original in zip(self.formset.change_forms, self.formset.get_queryset()):
682@@ -787,7 +791,7 @@
683 
684     def fields(self):
685         for field_name in flatten_fieldsets(self.fieldsets):
686-            yield self.formset.form_class.base_fields[field_name]
687+            yield self.formset.base_fields[field_name]
688 
689 class InlineAdminForm(AdminForm):
690     """
691Index: django/contrib/admin/templates/admin/edit_inline/tabular.html
692===================================================================
693--- django/contrib/admin/templates/admin/edit_inline/tabular.html       (revision 6962)
694+++ django/contrib/admin/templates/admin/edit_inline/tabular.html       (working copy)
695@@ -11,7 +11,7 @@
696          <th {% if forloop.first %}colspan="2"{% endif %}>{{ field.label|capfirst|escape }}</th>
697         {% endif %}
698      {% endfor %}
699-     {% if inline_admin_formset.formset.deletable %}<th>{% trans "Delete" %}?</th>{% endif %}
700+     {% if inline_admin_formset.deletable %}<th>{% trans "Delete" %}?</th>{% endif %}
701      </tr></thead>
702   
703      {% for inline_admin_form in inline_admin_formset %}
704@@ -45,7 +45,7 @@
705           {% endfor %}
706         {% endfor %}
707                 
708-        {% if inline_admin_formset.formset.deletable %}<td class="delete">{{ inline_admin_form.deletion_field.field }}</td>{% endif %}
709+        {% if inline_admin_formset.deletable %}<td class="delete">{{ inline_admin_form.deletion_field.field }}</td>{% endif %}
710         
711         </tr>
712 
713Index: tests/modeltests/model_formsets/models.py
714===================================================================
715--- tests/modeltests/model_formsets/models.py   (revision 6962)
716+++ tests/modeltests/model_formsets/models.py   (working copy)
717@@ -16,12 +16,35 @@
718 
719 __test__ = {'API_TESTS': """
720 
721->>> from django.newforms.models import formset_for_model
722+>>> from django import newforms as forms
723+>>> from django.newforms.models import formset_for_model, ModelFormSet
724 
725->>> qs = Author.objects.all()
726->>> AuthorFormSet = formset_for_model(Author, extra=3)
727+A bare bones verion.
728 
729->>> formset = AuthorFormSet(qs)
730+>>> class AuthorFormSet(ModelFormSet):
731+...     class Meta:
732+...         model = Author
733+>>> AuthorFormSet.base_fields.keys()
734+['name']
735+
736+Extra fields.
737+
738+>>> class AuthorFormSet(ModelFormSet):
739+...     published = forms.BooleanField()
740+...
741+...     class Meta:
742+...         model = Author
743+>>> AuthorFormSet.base_fields.keys()
744+['name', 'published']
745+
746+Lets create a formset that is bound to a model.
747+
748+>>> class AuthorFormSet(ModelFormSet):
749+...     class Meta:
750+...         model = Author
751+...         num_extra = 3
752+
753+>>> formset = AuthorFormSet()
754 >>> for form in formset.forms:
755 ...     print form.as_p()
756 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></p>
757@@ -35,7 +58,7 @@
758 ...     'form-2-name': '',
759 ... }
760 
761->>> formset = AuthorFormSet(qs, data=data)
762+>>> formset = AuthorFormSet(data)
763 >>> formset.is_valid()
764 True
765 
766@@ -49,14 +72,21 @@
767 
768 
769 Gah! We forgot Paul Verlaine. Let's create a formset to edit the existing
770-authors with an extra form to add him. This time we'll use formset_for_queryset.
771-We *could* use formset_for_queryset to restrict the Author objects we edit,
772-but in that case we'll use it to display them in alphabetical order by name.
773+authors with an extra form to add him. When subclassing ModelFormSet you can
774+override the get_queryset method to return any queryset we like, but in this
775+case we'll use it to display it in alphabetical order by name.
776 
777->>> qs = Author.objects.order_by('name')
778->>> AuthorFormSet = formset_for_model(Author, extra=1, deletable=False)
779+>>> class AuthorFormSet(ModelFormSet):
780+...     class Meta:
781+...         model = Author
782+...         num_extra = 1
783+...         deletable = False
784+...
785+...     def get_queryset(self, **kwargs):
786+...         qs = super(AuthorFormSet, self).get_queryset(**kwargs)
787+...         return qs.order_by('name')
788 
789->>> formset = AuthorFormSet(qs)
790+>>> formset = AuthorFormSet()
791 >>> for form in formset.forms:
792 ...     print form.as_p()
793 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /><input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /></p>
794@@ -73,7 +103,7 @@
795 ...     'form-2-name': 'Paul Verlaine',
796 ... }
797 
798->>> formset = AuthorFormSet(qs, data=data)
799+>>> formset = AuthorFormSet(data)
800 >>> formset.is_valid()
801 True
802 
803@@ -90,10 +120,17 @@
804 This probably shouldn't happen, but it will. If an add form was marked for
805 deltetion, make sure we don't save that form.
806 
807->>> qs = Author.objects.order_by('name')
808->>> AuthorFormSet = formset_for_model(Author, extra=1, deletable=True)
809+>>> class AuthorFormSet(ModelFormSet):
810+...     class Meta:
811+...         model = Author
812+...         num_extra = 1
813+...         deletable = True
814+...
815+...     def get_queryset(self, **kwargs):
816+...         qs = super(AuthorFormSet, self).get_queryset(**kwargs)
817+...         return qs.order_by('name')
818 
819->>> formset = AuthorFormSet(qs)
820+>>> formset = AuthorFormSet()
821 >>> for form in formset.forms:
822 ...     print form.as_p()
823 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Arthur Rimbaud" maxlength="100" /></p>
824@@ -117,7 +154,7 @@
825 ...     'form-3-DELETE': 'on',
826 ... }
827 
828->>> formset = AuthorFormSet(qs, data=data)
829+>>> formset = AuthorFormSet(data)
830 >>> formset.is_valid()
831 True
832 
833@@ -134,12 +171,18 @@
834 We can also create a formset that is tied to a parent model. This is how the
835 admin system's edit inline functionality works.
836 
837->>> from django.newforms.models import inline_formset
838+>>> from django.newforms.models import inline_formset, InlineFormset
839 
840->>> AuthorBooksFormSet = inline_formset(Author, Book, deletable=False, extra=3)
841+>>> class AuthorBooksFormSet(InlineFormset):
842+...     class Meta:
843+...         parent_model = Author
844+...         model = Book
845+...         num_extra = 3
846+...         deletable = False
847+
848 >>> author = Author.objects.get(name='Charles Baudelaire')
849 
850->>> formset = AuthorBooksFormSet(author)
851+>>> formset = AuthorBooksFormSet(instance=author)
852 >>> for form in formset.forms:
853 ...     print form.as_p()
854 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" maxlength="100" /><input type="hidden" name="book_set-0-id" id="id_book_set-0-id" /></p>
855@@ -153,7 +196,7 @@
856 ...     'book_set-2-title': '',
857 ... }
858 
859->>> formset = AuthorBooksFormSet(author, data=data)
860+>>> formset = AuthorBooksFormSet(data, instance=author)
861 >>> formset.is_valid()
862 True
863 
864@@ -169,10 +212,16 @@
865 one. This time though, an edit form will be available for every existing
866 book.
867 
868->>> AuthorBooksFormSet = inline_formset(Author, Book, deletable=False, extra=2)
869+>>> class AuthorBooksFormSet(InlineFormset):
870+...     class Meta:
871+...         parent_model = Author
872+...         model = Book
873+...         num_extra = 2
874+...         deletable = False
875+
876 >>> author = Author.objects.get(name='Charles Baudelaire')
877 
878->>> formset = AuthorBooksFormSet(author)
879+>>> formset = AuthorBooksFormSet(instance=author)
880 >>> for form in formset.forms:
881 ...     print form.as_p()
882 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p>
883@@ -187,7 +236,7 @@
884 ...     'book_set-2-title': '',
885 ... }
886 
887->>> formset = AuthorBooksFormSet(author, data=data)
888+>>> formset = AuthorBooksFormSet(data, instance=author)
889 >>> formset.is_valid()
890 True
891 
892Index: tests/regressiontests/forms/formsets.py
893===================================================================
894--- tests/regressiontests/forms/formsets.py     (revision 6962)
895+++ tests/regressiontests/forms/formsets.py     (working copy)
896@@ -2,19 +2,18 @@
897 formset_tests = """
898 # Basic FormSet creation and usage ############################################
899 
900-FormSet allows us to use multiple instance of the same form on 1 page. For now,
901-the best way to create a FormSet is by using the formset_for_form function.
902+FormSet allows us to use multiple instance of the same form on 1 page. Create
903+the formset as you would a regular form by defining the fields declaratively.
904 
905 >>> from django.newforms import Form, CharField, IntegerField, ValidationError
906->>> from django.newforms.formsets import formset_for_form, BaseFormSet
907+>>> from django.newforms import BooleanField
908+>>> from django.newforms.formsets import formset_for_form, BaseFormSet, FormSet
909 
910->>> class Choice(Form):
911+>>> class ChoiceFormSet(FormSet):
912 ...     choice = CharField()
913 ...     votes = IntegerField()
914 
915->>> ChoiceFormSet = formset_for_form(Choice)
916 
917-
918 A FormSet constructor takes the same arguments as Form. Let's create a FormSet
919 for adding data. By default, it displays 1 blank form. It can display more,
920 but we'll look at how to do so later.
921@@ -145,15 +144,31 @@
922 >>> formset.errors
923 [{'votes': [u'This field is required.'], 'choice': [u'This field is required.']}]
924 
925+# Subclassing a FormSet class #################################################
926 
927+We can subclass a FormSet to add addition fields to an already exisiting
928+FormSet.
929+
930+>>> class SecondChoiceFormSet(ChoiceFormSet):
931+...     is_public = BooleanField()
932+
933+>>> formset = SecondChoiceFormSet(auto_id=False, prefix="choices")
934+>>> for form in formset.forms:
935+...     print form.as_ul()
936+<li>Choice: <input type="text" name="choices-0-choice" /></li>
937+<li>Votes: <input type="text" name="choices-0-votes" /></li>
938+<li>Is public: <input type="checkbox" name="choices-0-is_public" /></li>
939+
940 # Displaying more than 1 blank form ###########################################
941 
942-We can also display more than 1 empty form at a time. To do so, pass a
943-num_extra argument to formset_for_form.
944+We can also display more than 1 empty form at a time. To do so, create an inner
945+Meta class with an attribute num_extra.
946 
947->>> ChoiceFormSet = formset_for_form(Choice, num_extra=3)
948+>>> class NumExtraChoiceFormSet(ChoiceFormSet):
949+...     class Meta:
950+...         num_extra = 3
951 
952->>> formset = ChoiceFormSet(auto_id=False, prefix='choices')
953+>>> formset = NumExtraChoiceFormSet(auto_id=False, prefix='choices')
954 >>> for form in formset.forms:
955 ...    print form.as_ul()
956 <li>Choice: <input type="text" name="choices-0-choice" /></li>
957@@ -177,7 +192,7 @@
958 ...     'choices-2-votes': '',
959 ... }
960 
961->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
962+>>> formset = NumExtraChoiceFormSet(data, auto_id=False, prefix='choices')
963 >>> formset.is_valid()
964 True
965 >>> formset.cleaned_data
966@@ -196,7 +211,7 @@
967 ...     'choices-2-votes': '',
968 ... }
969 
970->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
971+>>> formset = NumExtraChoiceFormSet(data, auto_id=False, prefix='choices')
972 >>> formset.is_valid()
973 True
974 >>> formset.cleaned_data
975@@ -215,7 +230,7 @@
976 ...     'choices-2-votes': '',
977 ... }
978 
979->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
980+>>> formset = NumExtraChoiceFormSet(data, auto_id=False, prefix='choices')
981 >>> formset.is_valid()
982 False
983 >>> formset.errors
984@@ -226,7 +241,7 @@
985 data.
986 
987 >>> initial = [{'choice': u'Calexico', 'votes': 100}]
988->>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
989+>>> formset = NumExtraChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
990 >>> for form in formset.forms:
991 ...    print form.as_ul()
992 <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
993@@ -254,7 +269,7 @@
994 ...     'choices-3-votes': '',
995 ... }
996 
997->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
998+>>> formset = NumExtraChoiceFormSet(data, auto_id=False, prefix='choices')
999 >>> formset.is_valid()
1000 False
1001 >>> formset.errors
1002@@ -263,15 +278,17 @@
1003 
1004 # FormSets with deletion ######################################################
1005 
1006-We can easily add deletion ability to a FormSet with an agrument to
1007-formset_for_form. This will add a boolean field to each form instance. When
1008-that boolean field is True, the cleaned data will be in formset.deleted_data
1009+We can easily add deletion ability to a FormSet by setting deletable to True
1010+in the inner Meta class. This will add a boolean field to each form instance.
1011+When that boolean field is True, the cleaned data will be in formset.deleted_data
1012 rather than formset.cleaned_data
1013 
1014->>> ChoiceFormSet = formset_for_form(Choice, deletable=True)
1015+>>> class DeletableChoiceFormSet(ChoiceFormSet):
1016+...     class Meta:
1017+...         deletable = True
1018 
1019 >>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
1020->>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
1021+>>> formset = DeletableChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
1022 >>> for form in formset.forms:
1023 ...    print form.as_ul()
1024 <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
1025@@ -300,7 +317,7 @@
1026 ...     'choices-2-DELETE': '',
1027 ... }
1028 
1029->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
1030+>>> formset = DeletableChoiceFormSet(data, auto_id=False, prefix='choices')
1031 >>> formset.is_valid()
1032 True
1033 >>> formset.cleaned_data
1034@@ -310,18 +327,20 @@
1035 
1036 # FormSets with ordering ######################################################
1037 
1038-We can also add ordering ability to a FormSet with an agrument to
1039-formset_for_form. This will add a integer field to each form instance. When
1040+We can also add ordering ability to a FormSet by setting orderable to True in
1041+the inner Meta class. This will add a integer field to each form instance. When
1042 form validation succeeds, formset.cleaned_data will have the data in the correct
1043 order specified by the ordering fields. If a number is duplicated in the set
1044 of ordering fields, for instance form 0 and form 3 are both marked as 1, then
1045 the form index used as a secondary ordering criteria. In order to put
1046 something at the front of the list, you'd need to set it's order to 0.
1047 
1048->>> ChoiceFormSet = formset_for_form(Choice, orderable=True)
1049+>>> class OrderableChoiceFormSet(ChoiceFormSet):
1050+...     class Meta:
1051+...         orderable = True
1052 
1053 >>> initial = [{'choice': u'Calexico', 'votes': 100}, {'choice': u'Fergie', 'votes': 900}]
1054->>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
1055+>>> formset = OrderableChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
1056 >>> for form in formset.forms:
1057 ...    print form.as_ul()
1058 <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
1059@@ -347,7 +366,7 @@
1060 ...     'choices-2-ORDER': '0',
1061 ... }
1062 
1063->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
1064+>>> formset = OrderableChoiceFormSet(data, auto_id=False, prefix='choices')
1065 >>> formset.is_valid()
1066 True
1067 >>> for cleaned_data in formset.cleaned_data:
1068@@ -359,15 +378,20 @@
1069 # FormSets with ordering + deletion ###########################################
1070 
1071 Let's try throwing ordering and deletion into the same form.
1072+TODO: Perhaps handle Meta class inheritance so you can subclass
1073+OrderableChoiceFormSet and DeletableChoiceFormSet?
1074 
1075->>> ChoiceFormSet = formset_for_form(Choice, orderable=True, deletable=True)
1076+>>> class MixedChoiceFormSet(ChoiceFormSet):
1077+...     class Meta:
1078+...         orderable = True
1079+...         deletable = True
1080 
1081 >>> initial = [
1082 ...     {'choice': u'Calexico', 'votes': 100},
1083 ...     {'choice': u'Fergie', 'votes': 900},
1084 ...     {'choice': u'The Decemberists', 'votes': 500},
1085 ... ]
1086->>> formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
1087+>>> formset = MixedChoiceFormSet(initial=initial, auto_id=False, prefix='choices')
1088 >>> for form in formset.forms:
1089 ...    print form.as_ul()
1090 <li>Choice: <input type="text" name="choices-0-choice" value="Calexico" /></li>
1091@@ -409,7 +433,7 @@
1092 ...     'choices-3-DELETE': '',
1093 ... }
1094 
1095->>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
1096+>>> formset = MixedChoiceFormSet(data, auto_id=False, prefix='choices')
1097 >>> formset.is_valid()
1098 True
1099 >>> for cleaned_data in formset.cleaned_data:
1100@@ -428,16 +452,14 @@
1101 Let's define a FormSet that takes a list of favorite drinks, but raises am
1102 error if there are any duplicates.
1103 
1104->>> class FavoriteDrinkForm(Form):
1105+>>> class FavoriteDrinksFormSet(FormSet):
1106 ...     name = CharField()
1107+...     
1108+...     class Meta:
1109+...         num_extra = 2
1110+...         orderable = False
1111+...         deletable = False
1112 ...
1113-
1114->>> class FavoriteDrinksFormSet(BaseFormSet):
1115-...     form_class = FavoriteDrinkForm
1116-...     num_extra = 2
1117-...     orderable = False
1118-...     deletable = False
1119-...
1120 ...     def clean(self):
1121 ...         seen_drinks = []
1122 ...         for drink in self.cleaned_data:
1123Index: tests/regressiontests/inline_formsets/models.py
1124===================================================================
1125--- tests/regressiontests/inline_formsets/models.py     (revision 6962)
1126+++ tests/regressiontests/inline_formsets/models.py     (working copy)
1127@@ -21,7 +21,7 @@
1128 Child has two ForeignKeys to Parent, so if we don't specify which one to use
1129 for the inline formset, we should get an exception.
1130 
1131->>> ifs = inline_formset(Parent, Child)
1132+>>> inline_formset(Parent, Child)()
1133 Traceback (most recent call last):
1134     ...
1135 Exception: <class 'regressiontests.inline_formsets.models.Child'> has more than 1 ForeignKey to <class 'regressiontests.inline_formsets.models.Parent'>
1136@@ -29,14 +29,14 @@
1137 
1138 These two should both work without a problem.
1139 
1140->>> ifs = inline_formset(Parent, Child, fk_name='mother')
1141->>> ifs = inline_formset(Parent, Child, fk_name='father')
1142+>>> ifs = inline_formset(Parent, Child, fk_name='mother')()
1143+>>> ifs = inline_formset(Parent, Child, fk_name='father')()
1144 
1145 
1146 If we specify fk_name, but it isn't a ForeignKey from the child model to the
1147 parent model, we should get an exception.
1148 
1149->>> ifs = inline_formset(Parent, Child, fk_name='school')
1150+>>> inline_formset(Parent, Child, fk_name='school')()
1151 Traceback (most recent call last):
1152     ...
1153 Exception: fk_name 'school' is not a ForeignKey to <class 'regressiontests.inline_formsets.models.Parent'>
1154@@ -45,7 +45,7 @@
1155 If the field specified in fk_name is not a ForeignKey, we should get an
1156 exception.
1157 
1158->>> ifs = inline_formset(Parent, Child, fk_name='test')
1159+>>> inline_formset(Parent, Child, fk_name='test')()
1160 Traceback (most recent call last):
1161     ...
1162 Exception: <class 'regressiontests.inline_formsets.models.Child'> has no field named 'test'