Ticket #8160: model_formsets.diff

File model_formsets.diff, 6.2 KB (added by justinf, 7 years ago)
  • django/forms/models.py

     
    220220
    221221def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
    222222                       formfield_callback=lambda f: f.formfield()):
    223     # HACK: we should be able to construct a ModelForm without creating
    224     # and passing in a temporary inner class
    225     class Meta:
    226         pass
    227     setattr(Meta, 'model', model)
    228     setattr(Meta, 'fields', fields)
    229     setattr(Meta, 'exclude', exclude)
     223    import types
     224    bases = ()
     225    if hasattr(form, 'Meta'):
     226        bases = (form.Meta, )
     227    Meta = types.ClassType('Meta', bases, {})
     228
     229    form_model = getattr(Meta, 'model', None)
     230    if form_model:
     231        if form_model != model:
     232            raise Exception('model %s does not match model %s for ModelForm %s' %
     233                (model, form_model, form))
     234    else:
     235        setattr(Meta, 'model', model)
     236
     237    if fields: setattr(Meta, 'fields', fields)
     238    if exclude: setattr(Meta, 'exclude', exclude)
     239
    230240    class_name = model.__name__ + 'Form'
    231241    return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
    232242                              'formfield_callback': formfield_callback})
     
    242252
    243253    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
    244254                 queryset=None, **kwargs):
    245         self.queryset = queryset
    246         defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
    247         if self.max_num > 0:
    248             qs = self.get_queryset()[:self.max_num]
    249         else:
    250             qs = self.get_queryset()
    251         defaults['initial'] = [model_to_dict(obj) for obj in qs]
     255        self.queryset = queryset or self.model._default_manager.get_query_set()
     256           
     257        defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix, 'initial': self.queryset}
    252258        defaults.update(kwargs)
    253259        super(BaseModelFormSet, self).__init__(**defaults)
    254260
    255261    def get_queryset(self):
    256         if self.queryset is not None:
    257             return self.queryset
    258         return self.model._default_manager.get_query_set()
     262        return self.queryset
    259263
    260264    def save_new(self, form, commit=True):
    261265        """Saves and returns a new model instance for the given form."""
     
    269273        """Saves model instances for every form, adding and changing instances
    270274        as necessary, and returns the list of instances.
    271275        """
     276        self.changed_objects = []
     277        self.deleted_objects = []
     278        self.new_objects = []
    272279        if not commit:
    273280            self.saved_forms = []
    274281            def save_m2m():
    275282                for form in self.saved_forms:
    276283                    form.save_m2m()
    277284            self.save_m2m = save_m2m
    278         return self.save_existing_objects(commit) + self.save_new_objects(commit)
    279285
    280     def save_existing_objects(self, commit=True):
    281         self.changed_objects = []
    282         self.deleted_objects = []
    283         if not self.get_queryset():
    284             return []
    285 
    286         # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk
    287         existing_objects = {}
    288         for obj in self.get_queryset():
    289             existing_objects[obj.pk] = obj
    290286        saved_instances = []
    291         for form in self.initial_forms:
    292             obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]]
     287        for form in self.forms:
    293288            if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
    294                 self.deleted_objects.append(obj)
    295                 obj.delete()
    296             else:
    297                 if form.changed_data:
    298                     self.changed_objects.append((obj, form.changed_data))
    299                     saved_instances.append(self.save_existing(form, obj, commit=commit))
     289                if form.instance:
     290                    self.deleted_objects.append(obj)
     291                    obj.delete()
     292                else:
     293                    continue
     294            elif form.changed_data:
     295                    if form.instance:
     296                        self.changed_objects.append((form.instance, form.changed_data))
     297                        saved_instances.append(form.save(commit=commit))
     298                    else:
     299                        self.new_objects.append(form.save(commit=commit))
    300300                    if not commit:
    301301                        self.saved_forms.append(form)
    302         return saved_instances
     302        return saved_instances + self.new_objects
    303303
    304     def save_new_objects(self, commit=True):
    305         self.new_objects = []
    306         for form in self.extra_forms:
    307             if not form.has_changed():
    308                 continue
    309             # If someone has marked an add form for deletion, don't save the
    310             # object.
    311             if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
    312                 continue
    313             self.new_objects.append(self.save_new(form, commit=commit))
    314             if not commit:
    315                 self.saved_forms.append(form)
    316         return self.new_objects
    317 
    318304    def add_fields(self, form, index):
    319305        """Add a hidden field for the object's primary key."""
    320306        if self.model._meta.has_auto_field:
     
    322308            form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput)
    323309        super(BaseModelFormSet, self).add_fields(form, index)
    324310
     311    def _construct_form(self, i, **kwargs):
     312        defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
     313        if self.data or self.files:
     314            defaults['data'] = self.data
     315            defaults['files'] = self.files
     316        # Allow extra forms to be empty.
     317        if i >= self._initial_form_count:
     318            defaults['empty_permitted'] = True
     319        else:
     320            defaults['instance'] = self.queryset[i]
     321           
     322        defaults.update(kwargs)
     323        form = self.form(**defaults)
     324        self.add_fields(form, i)
     325        return form
     326
     327
    325328def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
    326329                         formset=BaseModelFormSet,
    327330                         extra=1, can_delete=False, can_order=False,
Back to Top