Loading django/forms/models.py +9 −3 Original line number Diff line number Diff line Loading @@ -564,6 +564,9 @@ class BaseModelFormSet(BaseFormSet): """ model = None # Set of fields that must be unique among forms of this set. unique_fields = set() def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, queryset=None, **kwargs): self.queryset = queryset Loading Loading @@ -677,9 +680,11 @@ class BaseModelFormSet(BaseFormSet): for uclass, unique_check in all_unique_checks: seen_data = set() for form in valid_forms: # get data for each field of each of unique_check row_data = (form.cleaned_data[field] for field in unique_check if field in form.cleaned_data) # Get the data for the set of fields that must be unique among the forms. row_data = ( field if field in self.unique_fields else form.cleaned_data[field] for field in unique_check if field in form.cleaned_data ) # Reduce Model instances to their primary key values row_data = tuple(d._get_pk_val() if hasattr(d, '_get_pk_val') else d for d in row_data) Loading Loading @@ -878,6 +883,7 @@ class BaseInlineFormSet(BaseModelFormSet): qs = queryset.filter(**{self.fk.name: self.instance}) else: qs = queryset.none() self.unique_fields = {self.fk.name} super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, queryset=qs, **kwargs) Loading tests/inline_formsets/models.py +3 −0 Original line number Diff line number Diff line Loading @@ -31,5 +31,8 @@ class Poem(models.Model): poet = models.ForeignKey(Poet, models.CASCADE) name = models.CharField(max_length=100) class Meta: unique_together = ('poet', 'name') def __str__(self): return self.name tests/inline_formsets/tests.py +14 −0 Original line number Diff line number Diff line Loading @@ -162,3 +162,17 @@ class InlineFormsetFactoryTest(TestCase): PoemFormSet = inlineformset_factory(Poet, Poem, fields="__all__", extra=0) formset = PoemFormSet(None, instance=poet) self.assertEqual(len(formset.forms), 1) def test_unsaved_fk_validate_unique(self): poet = Poet(name='unsaved') PoemFormSet = inlineformset_factory(Poet, Poem, fields=['name']) data = { 'poem_set-TOTAL_FORMS': '2', 'poem_set-INITIAL_FORMS': '0', 'poem_set-MAX_NUM_FORMS': '2', 'poem_set-0-name': 'Poem', 'poem_set-1-name': 'Poem', } formset = PoemFormSet(data, instance=poet) self.assertFalse(formset.is_valid()) self.assertEqual(formset.non_form_errors(), ['Please correct the duplicate data for name.']) Loading
django/forms/models.py +9 −3 Original line number Diff line number Diff line Loading @@ -564,6 +564,9 @@ class BaseModelFormSet(BaseFormSet): """ model = None # Set of fields that must be unique among forms of this set. unique_fields = set() def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, queryset=None, **kwargs): self.queryset = queryset Loading Loading @@ -677,9 +680,11 @@ class BaseModelFormSet(BaseFormSet): for uclass, unique_check in all_unique_checks: seen_data = set() for form in valid_forms: # get data for each field of each of unique_check row_data = (form.cleaned_data[field] for field in unique_check if field in form.cleaned_data) # Get the data for the set of fields that must be unique among the forms. row_data = ( field if field in self.unique_fields else form.cleaned_data[field] for field in unique_check if field in form.cleaned_data ) # Reduce Model instances to their primary key values row_data = tuple(d._get_pk_val() if hasattr(d, '_get_pk_val') else d for d in row_data) Loading Loading @@ -878,6 +883,7 @@ class BaseInlineFormSet(BaseModelFormSet): qs = queryset.filter(**{self.fk.name: self.instance}) else: qs = queryset.none() self.unique_fields = {self.fk.name} super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, queryset=qs, **kwargs) Loading
tests/inline_formsets/models.py +3 −0 Original line number Diff line number Diff line Loading @@ -31,5 +31,8 @@ class Poem(models.Model): poet = models.ForeignKey(Poet, models.CASCADE) name = models.CharField(max_length=100) class Meta: unique_together = ('poet', 'name') def __str__(self): return self.name
tests/inline_formsets/tests.py +14 −0 Original line number Diff line number Diff line Loading @@ -162,3 +162,17 @@ class InlineFormsetFactoryTest(TestCase): PoemFormSet = inlineformset_factory(Poet, Poem, fields="__all__", extra=0) formset = PoemFormSet(None, instance=poet) self.assertEqual(len(formset.forms), 1) def test_unsaved_fk_validate_unique(self): poet = Poet(name='unsaved') PoemFormSet = inlineformset_factory(Poet, Poem, fields=['name']) data = { 'poem_set-TOTAL_FORMS': '2', 'poem_set-INITIAL_FORMS': '0', 'poem_set-MAX_NUM_FORMS': '2', 'poem_set-0-name': 'Poem', 'poem_set-1-name': 'Poem', } formset = PoemFormSet(data, instance=poet) self.assertFalse(formset.is_valid()) self.assertEqual(formset.non_form_errors(), ['Please correct the duplicate data for name.'])