mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Fixed #25987 -- Made inline formset validation respect unique_together with an unsaved parent object.
Thanks Anton Kuzmichev for the report and Tim for the review.
This commit is contained in:
		| @@ -564,6 +564,9 @@ class BaseModelFormSet(BaseFormSet): | |||||||
|     """ |     """ | ||||||
|     model = None |     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, |     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, | ||||||
|                  queryset=None, **kwargs): |                  queryset=None, **kwargs): | ||||||
|         self.queryset = queryset |         self.queryset = queryset | ||||||
| @@ -677,9 +680,11 @@ class BaseModelFormSet(BaseFormSet): | |||||||
|         for uclass, unique_check in all_unique_checks: |         for uclass, unique_check in all_unique_checks: | ||||||
|             seen_data = set() |             seen_data = set() | ||||||
|             for form in valid_forms: |             for form in valid_forms: | ||||||
|                 # get data for each field of each of unique_check |                 # Get the data for the set of fields that must be unique among the forms. | ||||||
|                 row_data = (form.cleaned_data[field] |                 row_data = ( | ||||||
|                             for field in unique_check if field in form.cleaned_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 |                 # Reduce Model instances to their primary key values | ||||||
|                 row_data = tuple(d._get_pk_val() if hasattr(d, '_get_pk_val') else d |                 row_data = tuple(d._get_pk_val() if hasattr(d, '_get_pk_val') else d | ||||||
|                                  for d in row_data) |                                  for d in row_data) | ||||||
| @@ -878,6 +883,7 @@ class BaseInlineFormSet(BaseModelFormSet): | |||||||
|             qs = queryset.filter(**{self.fk.name: self.instance}) |             qs = queryset.filter(**{self.fk.name: self.instance}) | ||||||
|         else: |         else: | ||||||
|             qs = queryset.none() |             qs = queryset.none() | ||||||
|  |         self.unique_fields = {self.fk.name} | ||||||
|         super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, |         super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix, | ||||||
|                                                 queryset=qs, **kwargs) |                                                 queryset=qs, **kwargs) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -31,5 +31,8 @@ class Poem(models.Model): | |||||||
|     poet = models.ForeignKey(Poet, models.CASCADE) |     poet = models.ForeignKey(Poet, models.CASCADE) | ||||||
|     name = models.CharField(max_length=100) |     name = models.CharField(max_length=100) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         unique_together = ('poet', 'name') | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.name |         return self.name | ||||||
|   | |||||||
| @@ -162,3 +162,17 @@ class InlineFormsetFactoryTest(TestCase): | |||||||
|         PoemFormSet = inlineformset_factory(Poet, Poem, fields="__all__", extra=0) |         PoemFormSet = inlineformset_factory(Poet, Poem, fields="__all__", extra=0) | ||||||
|         formset = PoemFormSet(None, instance=poet) |         formset = PoemFormSet(None, instance=poet) | ||||||
|         self.assertEqual(len(formset.forms), 1) |         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.']) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user