Commit e2518fdf authored by Stephen Burrows's avatar Stephen Burrows Committed by Tim Graham
Browse files

Fixed #12337 - Honor ModelForm.Meta.exclude when saving ManyToManyFields.

Thanks margieroginski for the report.
parent b67f2ac8
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -85,6 +85,8 @@ def save_instance(form, instance, fields=None, fail_message='saved',
        for f in opts.many_to_many:
            if fields and f.name not in fields:
                continue
            if exclude and f.name in exclude:
                continue
            if f.name in cleaned_data:
                f.save_form_data(instance, cleaned_data[f.name])
    if commit:
@@ -405,7 +407,8 @@ class BaseModelForm(BaseForm):
        else:
            fail_message = 'changed'
        return save_instance(self, self.instance, self._meta.fields,
                             fail_message, commit, construct=False)
                             fail_message, commit, self._meta.exclude,
                             construct=False)

    save.alters_data = True

+37 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ import datetime

from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import models
from django.forms import Form, ModelForm, FileField, ModelChoiceField
from django.forms import Form, ModelForm, FileField, ModelChoiceField, CharField
from django.forms.models import ModelFormMetaclass
from django.test import TestCase
from django.utils import six
@@ -26,6 +26,14 @@ class OptionalMultiChoiceModelForm(ModelForm):
        fields = '__all__'


class ChoiceFieldExclusionForm(ModelForm):
    multi_choice = CharField(max_length=50)

    class Meta:
        exclude = ['multi_choice']
        model = ChoiceFieldModel


class FileForm(Form):
    file1 = FileField()

@@ -221,3 +229,31 @@ class RelatedModelFormTests(TestCase):
            model=A

        self.assertTrue(issubclass(ModelFormMetaclass(str('Form'), (ModelForm,), {'Meta': Meta}), ModelForm))


class ManyToManyExclusionTestCase(TestCase):
    def test_m2m_field_exclusion(self):
        # Issue 12337. save_instance should honor the passed-in exclude keyword.
        opt1 = ChoiceOptionModel.objects.create(id=1, name='default')
        opt2 = ChoiceOptionModel.objects.create(id=2, name='option 2')
        opt3 = ChoiceOptionModel.objects.create(id=3, name='option 3')
        initial = {
            'choice': opt1,
            'choice_int': opt1,
        }
        data = {
            'choice': opt2.pk,
            'choice_int': opt2.pk,
            'multi_choice': 'string data!',
            'multi_choice_int': [opt1.pk],
        }
        instance = ChoiceFieldModel.objects.create(**initial)
        instance.multi_choice = instance.multi_choice_int = [opt2, opt3]
        form = ChoiceFieldExclusionForm(data=data, instance=instance)
        self.assertTrue(form.is_valid())
        self.assertEqual(form.cleaned_data['multi_choice'], data['multi_choice'])
        form.save()
        self.assertEqual(form.instance.choice.pk, data['choice'])
        self.assertEqual(form.instance.choice_int.pk, data['choice_int'])
        self.assertEqual(list(form.instance.multi_choice.all()), [opt2, opt3])
        self.assertEqual([obj.pk for obj in form.instance.multi_choice_int.all()], data['multi_choice_int'])