Ticket #8719: primary_key_formset_fixes.2.diff
File primary_key_formset_fixes.2.diff, 12.3 KB (added by , 16 years ago) |
---|
-
django/contrib/admin/helpers.py
diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 7a60943..56b4f56 100644
a b class InlineAdminForm(AdminForm): 126 126 super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields) 127 127 128 128 def pk_field(self): 129 return AdminField(self.form, self.formset._pk_field _name, False)129 return AdminField(self.form, self.formset._pk_field.name, False) 130 130 131 131 def deletion_field(self): 132 132 from django.forms.formsets import DELETION_FIELD_NAME -
django/forms/models.py
diff --git a/django/forms/models.py b/django/forms/models.py index bd9597d..3d551b8 100644
a b __all__ = ( 21 21 ) 22 22 23 23 def save_instance(form, instance, fields=None, fail_message='saved', 24 commit=True ):24 commit=True, exclude=None): 25 25 """ 26 26 Saves bound Form ``form``'s cleaned_data into model instance ``instance``. 27 27 … … def save_instance(form, instance, fields=None, fail_message='saved', 36 36 cleaned_data = form.cleaned_data 37 37 for f in opts.fields: 38 38 if not f.editable or isinstance(f, models.AutoField) \ 39 or (isinstance(f, models.OneToOneField) and f.rel.parent_link) \ 39 40 or not f.name in cleaned_data: 40 41 continue 41 42 if fields and f.name not in fields: 42 43 continue 44 if exclude and f.name in exclude: 45 continue 43 46 f.save_form_data(instance, cleaned_data[f.name]) 44 47 # Wrap up the saving of m2m data as a function. 45 48 def save_m2m(): … … def model_to_dict(instance, fields=None, exclude=None): 115 118 else: 116 119 # MultipleChoiceWidget needs a list of pks, not object instances. 117 120 data[f.name] = [obj.pk for obj in f.value_from_object(instance)] 118 elif isinstance(f, OneToOneField):119 data[f.attname] = f.value_from_object(instance)120 121 else: 121 122 data[f.name] = f.value_from_object(instance) 122 123 return data … … class BaseModelFormSet(BaseFormSet): 291 292 existing_objects[obj.pk] = obj 292 293 saved_instances = [] 293 294 for form in self.initial_forms: 294 obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]] 295 cleaned_pk = form.cleaned_data[self._pk_field.name] 296 if hasattr(cleaned_pk, "pk"): 297 obj = existing_objects[cleaned_pk.pk] 298 else: 299 obj = existing_objects[cleaned_pk] 295 300 if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: 296 301 self.deleted_objects.append(obj) 297 302 obj.delete() … … class BaseModelFormSet(BaseFormSet): 319 324 320 325 def add_fields(self, form, index): 321 326 """Add a hidden field for the object's primary key.""" 322 if self.model._meta.pk.auto_created: 323 self._pk_field_name = self.model._meta.pk.attname 324 form.fields[self._pk_field_name] = IntegerField(required=False, widget=HiddenInput) 327 from django.db.models import OneToOneField 328 pk = self.model._meta.pk 329 while isinstance(pk, OneToOneField): 330 pk = pk.rel.to._meta.pk # drill down until we find a "real" pk 331 self._pk_field = self.model._meta.pk 332 if pk.auto_created: 333 form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput) 325 334 super(BaseModelFormSet, self).add_fields(form, index) 326 335 327 336 def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(), … … class BaseInlineFormSet(BaseModelFormSet): 369 378 def save_new(self, form, commit=True): 370 379 kwargs = {self.fk.get_attname(): self.instance.pk} 371 380 new_obj = self.model(**kwargs) 372 return save_instance(form, new_obj, commit=commit) 381 exclude = [] 382 if self.fk == self._pk_field: 383 exclude.append(self.fk.name) 384 return save_instance(form, new_obj, exclude=exclude, commit=commit) 385 386 def add_fields(self, form, index): 387 from django.db.models import OneToOneField 388 super(BaseInlineFormSet, self).add_fields(form, index) 389 if isinstance(self._pk_field, OneToOneField): 390 queryset = self.instance.__class__._default_manager.all() 391 form.fields[self._pk_field.name] = ModelChoiceField(queryset, 392 required=False, widget=HiddenInput) 373 393 374 394 def _get_foreign_key(parent_model, model, fk_name=None): 375 395 """ … … def inlineformset_factory(parent_model, model, form=ModelForm, 419 439 """ 420 440 fk = _get_foreign_key(parent_model, model, fk_name=fk_name) 421 441 # let the formset handle object deletion by default 422 423 if exclude is not None: 424 exclude.append(fk.name) 425 else: 426 exclude = [fk.name] 442 443 def _formfield_callback(f, **kwargs): 444 if fk == model._meta.pk: 445 if f.primary_key: 446 return None 447 return formfield_callback(f, **kwargs) 448 449 if fk != model._meta.pk: 450 if exclude is not None: 451 exclude.append(fk.name) 452 else: 453 exclude = [fk.name] 454 427 455 FormSet = modelformset_factory(model, form=form, 428 formfield_callback= formfield_callback,456 formfield_callback=_formfield_callback, 429 457 formset=formset, 430 458 extra=extra, can_delete=can_delete, can_order=can_order, 431 459 fields=fields, exclude=exclude, max_num=max_num) -
tests/modeltests/model_forms/models.py
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index b8d3f93..bff869f 100644
a b class ImprovedArticle(models.Model): 66 66 class ImprovedArticleWithParentLink(models.Model): 67 67 article = models.OneToOneField(Article, parent_link=True) 68 68 69 class ImprovedArticleWithOneToOnePK(models.Model): 70 article = models.OneToOneField(Article, primary_key=True) 71 72 def __unicode__(self): 73 return self.article.headline 74 69 75 class BetterWriter(Writer): 70 76 pass 71 77 … … ValidationError: [u'Select a valid choice. 4 is not one of the available choices 811 817 >>> bw = BetterWriter(name=u'Joe Better') 812 818 >>> bw.save() 813 819 >>> sorted(model_to_dict(bw).keys()) 814 ['id', 'name', 'writer_ptr_id'] 820 ['id', 'name', 'writer_ptr'] 821 822 >>> class ImprovedArticleWithOneToOnePKForm(ModelForm): 823 ... class Meta: 824 ... model = ImprovedArticleWithOneToOnePK 825 826 >>> print ImprovedArticleWithOneToOnePKForm().as_p() 827 <p><label for="id_article">Article:</label> <select name="article" id="id_article"> 828 <option value="" selected="selected">---------</option> 829 <option value="1">New headline</option> 830 <option value="2">The walrus was Paul</option> 831 <option value="3">The walrus was Paul</option> 832 <option value="4">The walrus was Paul</option> 833 </select></p> 834 835 >>> form = ImprovedArticleWithOneToOnePKForm(data={'article': u'1'}) 836 >>> instance = form.save() 837 >>> instance 838 <ImprovedArticleWithOneToOnePK: New headline> 839 840 >>> form = ImprovedArticleWithOneToOnePKForm(instance=instance) 841 >>> print form.as_p() 842 <p><label for="id_article">Article:</label> <select name="article" id="id_article"> 843 <option value="">---------</option> 844 <option value="1" selected="selected">New headline</option> 845 <option value="2">The walrus was Paul</option> 846 <option value="3">The walrus was Paul</option> 847 <option value="4">The walrus was Paul</option> 848 </select></p> 815 849 816 850 # PhoneNumberField ############################################################ 817 851 -
tests/modeltests/model_formsets/models.py
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index 65987c1..6b00701 100644
a b class CustomPrimaryKey(models.Model): 36 36 my_pk = models.CharField(max_length=10, primary_key=True) 37 37 some_field = models.CharField(max_length=100) 38 38 39 40 39 # models for inheritance tests. 41 40 42 41 class Place(models.Model): … … class Place(models.Model): 47 46 return self.name 48 47 49 48 class Owner(models.Model): 49 auto_id = models.AutoField(primary_key=True) 50 50 name = models.CharField(max_length=100) 51 51 place = models.ForeignKey(Place) 52 53 def __unicode__(self): 54 return "%s at %s" % (self.name, self.place) 52 55 53 56 class Restaurant(Place): 54 57 serves_pizza = models.BooleanField() … … used. 262 265 >>> for form in formset.forms: 263 266 ... print form.as_p() 264 267 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" maxlength="100" /></p> 265 <p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr _id" id="id_form-0-author_ptr_id" /></p>268 <p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" id="id_form-0-author_ptr" /></p> 266 269 267 270 >>> data = { 268 271 ... 'form-TOTAL_FORMS': '1', # the number of forms rendered 269 272 ... 'form-INITIAL_FORMS': '0', # the number of forms with initial data 270 ... 'form-0-author_ptr _id': '',273 ... 'form-0-author_ptr': '', 271 274 ... 'form-0-name': 'Ernest Hemingway', 272 275 ... 'form-0-write_speed': '10', 273 276 ... } … … True 283 286 >>> for form in formset.forms: 284 287 ... print form.as_p() 285 288 <p><label for="id_form-0-name">Name:</label> <input id="id_form-0-name" type="text" name="form-0-name" value="Ernest Hemingway" maxlength="100" /></p> 286 <p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr _id" value="..." id="id_form-0-author_ptr_id" /></p>289 <p><label for="id_form-0-write_speed">Write speed:</label> <input type="text" name="form-0-write_speed" value="10" id="id_form-0-write_speed" /><input type="hidden" name="form-0-author_ptr" value="..." id="id_form-0-author_ptr" /></p> 287 290 <p><label for="id_form-1-name">Name:</label> <input id="id_form-1-name" type="text" name="form-1-name" maxlength="100" /></p> 288 <p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr _id" id="id_form-1-author_ptr_id" /></p>291 <p><label for="id_form-1-write_speed">Write speed:</label> <input type="text" name="form-1-write_speed" id="id_form-1-write_speed" /><input type="hidden" name="form-1-author_ptr" id="id_form-1-author_ptr" /></p> 289 292 290 293 >>> data = { 291 294 ... 'form-TOTAL_FORMS': '2', # the number of forms rendered 292 295 ... 'form-INITIAL_FORMS': '1', # the number of forms with initial data 293 ... 'form-0-author_ptr _id': hemingway_id,296 ... 'form-0-author_ptr': hemingway_id, 294 297 ... 'form-0-name': 'Ernest Hemingway', 295 298 ... 'form-0-write_speed': '10', 296 ... 'form-1-author_ptr _id': '',299 ... 'form-1-author_ptr': '', 297 300 ... 'form-1-name': '', 298 301 ... 'form-1-write_speed': '', 299 302 ... } … … We need to ensure that it is displayed 419 422 <p><label for="id_form-0-my_pk">My pk:</label> <input id="id_form-0-my_pk" type="text" name="form-0-my_pk" maxlength="10" /></p> 420 423 <p><label for="id_form-0-some_field">Some field:</label> <input id="id_form-0-some_field" type="text" name="form-0-some_field" maxlength="100" /></p> 421 424 425 # aa ########################################################################## 426 427 >>> place = Place(name=u'Giordanos', city=u'Chicago') 428 >>> place.save() 429 430 >>> FormSet = inlineformset_factory(Place, Owner, extra=1, can_delete=False) 431 >>> formset = FormSet(instance=place) 432 >>> for form in formset.forms: 433 ... print form.as_p() 434 <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" /></p> 435 436 >>> data = { 437 ... 'owner_set-TOTAL_FORMS': '1', 438 ... 'owner_set-INITIAL_FORMS': '0', 439 ... 'owner_set-0-name': u'Joe Perry', 440 ... } 441 >>> formset = FormSet(data, instance=place) 442 >>> formset.is_valid() 443 True 444 >>> formset.save() 445 [<Owner: Joe Perry at Giordanos>] 446 422 447 # Foreign keys in parents ######################################## 423 448 424 449 >>> from django.forms.models import _get_foreign_key