diff -r f681ed7ea9ad django/db/models/fields/related.py
a
|
b
|
|
824 | 824 | def validate(self, value, model_instance): |
825 | 825 | if self.rel.parent_link: |
826 | 826 | return |
827 | | # Don't validate the field if a value wasn't supplied. This is |
828 | | # generally the case when saving new inlines in the admin. |
829 | | # See #12507. |
| 827 | super(ForeignKey, self).validate(value, model_instance) |
830 | 828 | if value is None: |
831 | 829 | return |
832 | | super(ForeignKey, self).validate(value, model_instance) |
833 | | if not value: |
834 | | return |
835 | 830 | |
836 | 831 | qs = self.rel.to._default_manager.filter(**{self.rel.field_name:value}) |
837 | 832 | qs = qs.complex_filter(self.rel.limit_choices_to) |
diff -r f681ed7ea9ad django/forms/models.py
a
|
b
|
|
316 | 316 | return self.cleaned_data |
317 | 317 | |
318 | 318 | def _post_clean(self): |
319 | | exclude = self._get_validation_exclusions() |
320 | 319 | opts = self._meta |
321 | | |
322 | 320 | # Update the model instance with self.cleaned_data. |
323 | 321 | self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) |
324 | 322 | |
| 323 | exclude = self._get_validation_exclusions() |
| 324 | |
| 325 | # Foreign Keys being used to represent inline relationships |
| 326 | # are excluded from basic field value validation. This is for two |
| 327 | # reasons: firstly, the value may not be supplied (#12507; the |
| 328 | # case of providing new values to the admin); secondly the |
| 329 | # object being referred to may not yet fully exist (#12749). |
| 330 | # However, these fields *must* be included in uniqueness checks, |
| 331 | # so this can't be part of _get_validation_exclusions(). |
| 332 | for f_name, field in self.fields.items(): |
| 333 | if isinstance(field, InlineForeignKeyField): |
| 334 | exclude.append(f_name) |
| 335 | |
325 | 336 | # Clean the model instance's fields. |
326 | 337 | try: |
327 | 338 | self.instance.clean_fields(exclude=exclude) |
… |
… |
|
762 | 773 | unique_check = [field for field in unique_check if field != self.fk.name] |
763 | 774 | return super(BaseInlineFormSet, self).get_unique_error_message(unique_check) |
764 | 775 | |
| 776 | |
765 | 777 | def _get_foreign_key(parent_model, model, fk_name=None, can_fail=False): |
766 | 778 | """ |
767 | 779 | Finds and returns the ForeignKey from model to parent if there is one |
diff -r f681ed7ea9ad tests/regressiontests/admin_inlines/models.py
a
|
b
|
|
88 | 88 | # only Inline media |
89 | 89 | admin.site.register(Holder3, inlines=[InnerInline3]) |
90 | 90 | |
| 91 | # Models for #12749 |
| 92 | |
| 93 | class Person(models.Model): |
| 94 | firstname = models.CharField(max_length=15) |
| 95 | |
| 96 | class OutfitItem(models.Model): |
| 97 | name = models.CharField(max_length=15) |
| 98 | |
| 99 | class Fashionista(models.Model): |
| 100 | person = models.OneToOneField(Person, primary_key=True) |
| 101 | weaknesses = models.ManyToManyField(OutfitItem, through='ShoppingWeakness', blank=True) |
| 102 | |
| 103 | class ShoppingWeakness(models.Model): |
| 104 | fashionista = models.ForeignKey(Fashionista) |
| 105 | item = models.ForeignKey(OutfitItem) |
| 106 | |
| 107 | class InlineWeakness(admin.TabularInline): |
| 108 | model = ShoppingWeakness |
| 109 | extra = 1 |
| 110 | |
| 111 | admin.site.register(Fashionista, inlines=[InlineWeakness]) |
| 112 | |
| 113 | |
91 | 114 | __test__ = {'API_TESTS': """ |
92 | 115 | |
93 | 116 | # Regression test for #9362 |
diff -r f681ed7ea9ad tests/regressiontests/admin_inlines/tests.py
a
|
b
|
|
3 | 3 | # local test models |
4 | 4 | from models import Holder, Inner, InnerInline |
5 | 5 | from models import Holder2, Inner2, Holder3, Inner3 |
6 | | |
| 6 | from models import Person, OutfitItem, Fashionista |
7 | 7 | |
8 | 8 | class TestInline(TestCase): |
9 | 9 | fixtures = ['admin-views-users.xml'] |
… |
… |
|
38 | 38 | % holder.id) |
39 | 39 | self.assertContains(response, '<label>Inner readonly label:</label>') |
40 | 40 | |
| 41 | def test_inline_primary(self): |
| 42 | person = Person.objects.create(firstname='Imelda') |
| 43 | item = OutfitItem.objects.create(name='Shoes') |
| 44 | # Imelda likes shoes, but can't cary her own bags. |
| 45 | data = { |
| 46 | 'shoppingweakness_set-TOTAL_FORMS': 1, |
| 47 | 'shoppingweakness_set-INITIAL_FORMS': 0, |
| 48 | 'shoppingweakness_set-MAX_NUM_FORMS': 0, |
| 49 | '_save': u'Save', |
| 50 | 'person': person.id, |
| 51 | 'max_weight': 0, |
| 52 | 'shoppingweakness_set-0-item': item.id, |
| 53 | } |
| 54 | response = self.client.post('/test_admin/admin/admin_inlines/fashionista/add/', data) |
| 55 | self.assertEqual(response.status_code, 302) |
| 56 | self.assertEqual(len(Fashionista.objects.filter(person__firstname='Imelda')), 1) |
41 | 57 | |
42 | 58 | class TestInlineMedia(TestCase): |
43 | 59 | fixtures = ['admin-views-users.xml'] |