Changeset 9297
- Timestamp:
- 10/31/08 17:07:05 (2 months ago)
- Files:
-
- django/trunk/django/contrib/admin/helpers.py (modified) (4 diffs)
- django/trunk/django/contrib/admin/templates/admin/edit_inline/stacked.html (modified) (1 diff)
- django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html (modified) (1 diff)
- django/trunk/django/forms/models.py (modified) (5 diffs)
- django/trunk/tests/modeltests/model_formsets/models.py (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/contrib/admin/helpers.py
r9243 r9297 110 110 def fields(self): 111 111 for field_name in flatten_fieldsets(self.fieldsets): 112 if self.formset.fk.name == field_name: 113 continue 112 114 yield self.formset.form.base_fields[field_name] 113 115 … … 131 133 super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields) 132 134 135 def __iter__(self): 136 for name, options in self.fieldsets: 137 yield InlineFieldset(self.formset, self.form, name, **options) 138 133 139 def field_count(self): 134 140 # tabular.html uses this function for colspan value. … … 143 149 def pk_field(self): 144 150 return AdminField(self.form, self.formset._pk_field.name, False) 151 152 def fk_field(self): 153 return AdminField(self.form, self.formset.fk.name, False) 145 154 146 155 def deletion_field(self): … … 152 161 return AdminField(self.form, ORDERING_FIELD_NAME, False) 153 162 163 class InlineFieldset(Fieldset): 164 def __init__(self, formset, *args, **kwargs): 165 self.formset = formset 166 super(InlineFieldset, self).__init__(*args, **kwargs) 167 168 def __iter__(self): 169 for field in self.fields: 170 if self.formset.fk.name == field: 171 continue 172 yield Fieldline(self.form, field) 173 154 174 class AdminErrorList(forms.util.ErrorList): 155 175 """ django/trunk/django/contrib/admin/templates/admin/edit_inline/stacked.html
r9243 r9297 19 19 {% endfor %} 20 20 {{ inline_admin_form.pk_field.field }} 21 {{ inline_admin_form.fk_field.field }} 21 22 </div> 22 23 {% endfor %} django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html
r9243 r9297 27 27 {% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original.content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %} 28 28 </p>{% endif %} 29 {{ inline_admin_form.pk_field.field }} 29 {{ inline_admin_form.pk_field.field }} {{ inline_admin_form.fk_field.field }} 30 30 {% spaceless %} 31 31 {% for fieldset in inline_admin_form %} django/trunk/django/forms/models.py
r9293 r9297 4 4 """ 5 5 6 from django.utils.encoding import smart_unicode 6 from django.utils.encoding import smart_unicode, force_unicode 7 7 from django.utils.datastructures import SortedDict 8 8 from django.utils.text import get_text_list, capfirst … … 469 469 form.data[form.add_prefix(self._pk_field.name)] = None 470 470 return form 471 471 472 472 def get_queryset(self): 473 473 """ … … 486 486 super(BaseInlineFormSet, self).add_fields(form, index) 487 487 if self._pk_field == self.fk: 488 form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput) 488 form.fields[self._pk_field.name] = InlineForeignKeyField(self.instance, pk_field=True) 489 else: 490 form.fields[self.fk.name] = InlineForeignKeyField(self.instance, label=form.fields[self.fk.name].label) 489 491 490 492 def _get_foreign_key(parent_model, model, fk_name=None): … … 538 540 if fk.unique: 539 541 max_num = 1 540 if exclude is not None:541 exclude = list(exclude)542 exclude.append(fk.name)543 else:544 exclude = [fk.name]545 542 kwargs = { 546 543 'form': form, … … 560 557 561 558 # Fields ##################################################################### 559 560 class InlineForeignKeyHiddenInput(HiddenInput): 561 def _has_changed(self, initial, data): 562 return False 563 564 class InlineForeignKeyField(Field): 565 """ 566 A basic integer field that deals with validating the given value to a 567 given parent instance in an inline. 568 """ 569 default_error_messages = { 570 'invalid_choice': _(u'The inline foreign key did not match the parent instance primary key.'), 571 } 572 573 def __init__(self, parent_instance, *args, **kwargs): 574 self.parent_instance = parent_instance 575 self.pk_field = kwargs.pop("pk_field", False) 576 if self.parent_instance is not None: 577 kwargs["initial"] = self.parent_instance.pk 578 kwargs["required"] = False 579 kwargs["widget"] = InlineForeignKeyHiddenInput 580 super(InlineForeignKeyField, self).__init__(*args, **kwargs) 581 582 def clean(self, value): 583 if value in EMPTY_VALUES: 584 if self.pk_field: 585 return None 586 # if there is no value act as we did before. 587 return self.parent_instance 588 # ensure the we compare the values as equal types. 589 if force_unicode(value) != force_unicode(self.parent_instance.pk): 590 raise ValidationError(self.error_messages['invalid_choice']) 591 if self.pk_field: 592 return self.parent_instance.pk 593 return self.parent_instance 562 594 563 595 class ModelChoiceIterator(object): django/trunk/tests/modeltests/model_formsets/models.py
r9293 r9297 96 96 class MexicanRestaurant(Restaurant): 97 97 serves_tacos = models.BooleanField() 98 99 # models for testing unique_together validation when a fk is involved and 100 # using inlineformset_factory. 101 class Repository(models.Model): 102 name = models.CharField(max_length=25) 103 104 def __unicode__(self): 105 return self.name 106 107 class Revision(models.Model): 108 repository = models.ForeignKey(Repository) 109 revision = models.CharField(max_length=40) 110 111 class Meta: 112 unique_together = (("repository", "revision"),) 113 114 def __unicode__(self): 115 return u"%s (%s)" % (self.revision, unicode(self.repository)) 98 116 99 117 # models for testing callable defaults (see bug #7975). If you define a model … … 376 394 >>> for form in formset.forms: 377 395 ... print form.as_p() 378 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" maxlength="100" /><input type="hidden" name="book_set-0- id" id="id_book_set-0-id" /></p>379 <p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1- id" id="id_book_set-1-id" /></p>380 <p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2- id" id="id_book_set-2-id" /></p>396 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" id="id_book_set-0-id" /></p> 397 <p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p> 398 <p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p> 381 399 382 400 >>> data = { … … 410 428 >>> for form in formset.forms: 411 429 ... print form.as_p() 412 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-0- id" value="1" id="id_book_set-0-id" /></p>413 <p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1- id" id="id_book_set-1-id" /></p>414 <p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2- id" id="id_book_set-2-id" /></p>430 <p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p> 431 <p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p> 432 <p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p> 415 433 416 434 >>> data = { … … 455 473 456 474 >>> new_author = Author.objects.create(name='Charles Baudelaire') 457 >>> formset .instance = new_author475 >>> formset = AuthorBooksFormSet(data, instance=new_author, save_as_new=True) 458 476 >>> [book for book in formset.save() if book.author.pk == new_author.pk] 459 477 [<Book: Les Fleurs du Mal>, <Book: Le Spleen de Paris>] … … 464 482 >>> for form in formset.forms: 465 483 ... print form.as_p() 466 <p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /><input type="hidden" name="test-0- id" id="id_test-0-id" /></p>467 <p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /><input type="hidden" name="test-1- id" id="id_test-1-id" /></p>484 <p><label for="id_test-0-title">Title:</label> <input id="id_test-0-title" type="text" name="test-0-title" maxlength="100" /><input type="hidden" name="test-0-author" id="id_test-0-author" /><input type="hidden" name="test-0-id" id="id_test-0-id" /></p> 485 <p><label for="id_test-1-title">Title:</label> <input id="id_test-1-title" type="text" name="test-1-title" maxlength="100" /><input type="hidden" name="test-1-author" id="id_test-1-author" /><input type="hidden" name="test-1-id" id="id_test-1-id" /></p> 468 486 469 487 # Test a custom primary key ################################################### … … 487 505 >>> for form in formset.forms: 488 506 ... print form.as_p() 489 <p><label for="id_owner_set-0-name">Name:</label> <input id="id_owner_set-0-name" type="text" name="owner_set-0-name" maxlength="100" /><input type="hidden" name="owner_set-0- auto_id" id="id_owner_set-0-auto_id" /></p>490 <p><label for="id_owner_set-1-name">Name:</label> <input id="id_owner_set-1-name" type="text" name="owner_set-1-name" maxlength="100" /><input type="hidden" name="owner_set-1- auto_id" id="id_owner_set-1-auto_id" /></p>507 <p><label for="id_owner_set-0-name">Name:</label> <input id="id_owner_set-0-name" type="text" name="owner_set-0-name" maxlength="100" /><input type="hidden" name="owner_set-0-place" value="1" id="id_owner_set-0-place" /><input type="hidden" name="owner_set-0-auto_id" id="id_owner_set-0-auto_id" /></p> 508 <p><label for="id_owner_set-1-name">Name:</label> <input id="id_owner_set-1-name" type="text" name="owner_set-1-name" maxlength="100" /><input type="hidden" name="owner_set-1-place" value="1" id="id_owner_set-1-place" /><input type="hidden" name="owner_set-1-auto_id" id="id_owner_set-1-auto_id" /></p> 491 509 492 510 >>> data = { … … 507 525 >>> for form in formset.forms: 508 526 ... print form.as_p() 509 <p><label for="id_owner_set-0-name">Name:</label> <input id="id_owner_set-0-name" type="text" name="owner_set-0-name" value="Joe Perry" maxlength="100" /><input type="hidden" name="owner_set-0- auto_id" value="1" id="id_owner_set-0-auto_id" /></p>510 <p><label for="id_owner_set-1-name">Name:</label> <input id="id_owner_set-1-name" type="text" name="owner_set-1-name" maxlength="100" /><input type="hidden" name="owner_set-1- auto_id" id="id_owner_set-1-auto_id" /></p>511 <p><label for="id_owner_set-2-name">Name:</label> <input id="id_owner_set-2-name" type="text" name="owner_set-2-name" maxlength="100" /><input type="hidden" name="owner_set-2- auto_id" id="id_owner_set-2-auto_id" /></p>527 <p><label for="id_owner_set-0-name">Name:</label> <input id="id_owner_set-0-name" type="text" name="owner_set-0-name" value="Joe Perry" maxlength="100" /><input type="hidden" name="owner_set-0-place" value="1" id="id_owner_set-0-place" /><input type="hidden" name="owner_set-0-auto_id" value="1" id="id_owner_set-0-auto_id" /></p> 528 <p><label for="id_owner_set-1-name">Name:</label> <input id="id_owner_set-1-name" type="text" name="owner_set-1-name" maxlength="100" /><input type="hidden" name="owner_set-1-place" value="1" id="id_owner_set-1-place" /><input type="hidden" name="owner_set-1-auto_id" id="id_owner_set-1-auto_id" /></p> 529 <p><label for="id_owner_set-2-name">Name:</label> <input id="id_owner_set-2-name" type="text" name="owner_set-2-name" maxlength="100" /><input type="hidden" name="owner_set-2-place" value="1" id="id_owner_set-2-place" /><input type="hidden" name="owner_set-2-auto_id" id="id_owner_set-2-auto_id" /></p> 512 530 513 531 >>> data = { … … 546 564 >>> for form in formset.forms: 547 565 ... print form.as_p() 548 <p><label for="id_ownerprofile-0-age">Age:</label> <input type="text" name="ownerprofile-0-age" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" id="id_ownerprofile-0-owner" /></p>566 <p><label for="id_ownerprofile-0-age">Age:</label> <input type="text" name="ownerprofile-0-age" id="id_ownerprofile-0-age" /><input type="hidden" name="ownerprofile-0-owner" value="1" id="id_ownerprofile-0-owner" /></p> 549 567 550 568 >>> data = { … … 584 602 ... print form.as_p() 585 603 <p><label for="id_location_set-0-lat">Lat:</label> <input id="id_location_set-0-lat" type="text" name="location_set-0-lat" maxlength="100" /></p> 586 <p><label for="id_location_set-0-lon">Lon:</label> <input id="id_location_set-0-lon" type="text" name="location_set-0-lon" maxlength="100" /><input type="hidden" name="location_set-0- id" id="id_location_set-0-id" /></p>604 <p><label for="id_location_set-0-lon">Lon:</label> <input id="id_location_set-0-lon" type="text" name="location_set-0-lon" maxlength="100" /><input type="hidden" name="location_set-0-place" value="1" id="id_location_set-0-place" /><input type="hidden" name="location_set-0-id" id="id_location_set-0-id" /></p> 587 605 588 606 # Foreign keys in parents ######################################## … … 647 665 [{'__all__': [u'Price with this Price and Quantity already exists.']}] 648 666 667 # unique_together with inlineformset_factory 668 # Also see bug #8882. 669 670 >>> repository = Repository.objects.create(name=u'Test Repo') 671 >>> FormSet = inlineformset_factory(Repository, Revision, extra=1) 672 >>> data = { 673 ... 'revision_set-TOTAL_FORMS': '1', 674 ... 'revision_set-INITIAL_FORMS': '0', 675 ... 'revision_set-0-repository': repository.pk, 676 ... 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', 677 ... 'revision_set-0-DELETE': '', 678 ... } 679 >>> formset = FormSet(data, instance=repository) 680 >>> formset.is_valid() 681 True 682 >>> formset.save() 683 [<Revision: 146239817507f148d448db38840db7c3cbf47c76 (Test Repo)>] 684 685 # attempt to save the same revision against against the same repo. 686 >>> data = { 687 ... 'revision_set-TOTAL_FORMS': '1', 688 ... 'revision_set-INITIAL_FORMS': '0', 689 ... 'revision_set-0-repository': repository.pk, 690 ... 'revision_set-0-revision': '146239817507f148d448db38840db7c3cbf47c76', 691 ... 'revision_set-0-DELETE': '', 692 ... } 693 >>> formset = FormSet(data, instance=repository) 694 >>> formset.is_valid() 695 False 696 >>> formset.errors 697 [{'__all__': [u'Revision with this Repository and Revision already exists.']}] 698 649 699 # Use of callable defaults (see bug #7975). 650 700 … … 661 711 >>> print form.as_p() 662 712 <p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /></p> 663 <p><label for="id_membership_set-0-karma">Karma:</label> <input type="text" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0- id" id="id_membership_set-0-id" /></p>713 <p><label for="id_membership_set-0-karma">Karma:</label> <input type="text" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0-person" value="1" id="id_membership_set-0-person" /><input type="hidden" name="membership_set-0-id" id="id_membership_set-0-id" /></p> 664 714 665 715 # test for validation with callable defaults. Validations rely on hidden fields
