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:
parent
2c125bded1
commit
1a403aa705
|
@ -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.'])
|
||||||
|
|
Loading…
Reference in New Issue