Ticket #9284: 9284.diff

File 9284.diff, 16.7 KB (added by jkocherhans, 6 years ago)

Fixed a typo

  • django/forms/formsets.py

    diff --git a/django/forms/formsets.py b/django/forms/formsets.py
    index 7a5cb8a..1960f21 100644
    a b class BaseFormSet(StrAndUnicode): 
    4040        self.error_class = error_class
    4141        self._errors = None
    4242        self._non_form_errors = None
    43         # initialization is different depending on whether we recieved data, initial, or nothing
    44         if data or files:
    45             self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
    46             if self.management_form.is_valid():
    47                 self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT]
    48                 self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
    49             else:
    50                 raise ValidationError('ManagementForm data is missing or has been tampered with')
    51         else:
    52             if initial:
    53                 self._initial_form_count = len(initial)
    54                 if self._initial_form_count > self.max_num and self.max_num > 0:
    55                     self._initial_form_count = self.max_num
    56                 self._total_form_count = self._initial_form_count + self.extra
    57             else:
    58                 self._initial_form_count = 0
    59                 self._total_form_count = self.extra
    60             if self._total_form_count > self.max_num and self.max_num > 0:
    61                 self._total_form_count = self.max_num
    62             initial = {TOTAL_FORM_COUNT: self._total_form_count,
    63                        INITIAL_FORM_COUNT: self._initial_form_count}
    64             self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)
    65 
    6643        # construct the forms in the formset
    6744        self._construct_forms()
    6845
    6946    def __unicode__(self):
    7047        return self.as_table()
    7148
     49    def _management_form(self):
     50        """Returns the ManagementForm instance for this FormSet."""
     51        if self.data or self.files:
     52            form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)
     53            if not form.is_valid():
     54                raise ValidationError('ManagementForm data is missing or has been tampered with')
     55        else:
     56            form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={
     57                TOTAL_FORM_COUNT: self._total_forms(),
     58                INITIAL_FORM_COUNT: self._initial_forms()
     59            })
     60        return form
     61    management_form = property(_management_form)
     62
     63    def _total_forms(self):
     64        """Returns the total number of forms in this FormSet."""
     65        if self.data or self.files:
     66            return self.management_form.cleaned_data[TOTAL_FORM_COUNT]
     67        else:
     68            total_forms = self._initial_forms() + self.extra
     69            if total_forms > self.max_num > 0:
     70                total_forms = self.max_num
     71        return total_forms
     72    _total_form_count = property(_total_forms)
     73
     74    def _initial_forms(self):
     75        """Returns the number of forms that are required in this FormSet."""
     76        if self.data or self.files:
     77            return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
     78        else:
     79            # Use the length of the inital data if it's there, 0 otherwise.
     80            initial_forms = self.initial and len(self.initial) or 0
     81            if initial_forms > self.max_num > 0:
     82                initial_forms = self.max_num
     83        return initial_forms
     84    _initial_form_count = property(_initial_forms)
     85
    7286    def _construct_forms(self):
    7387        # instantiate all the forms and put them in self.forms
    7488        self.forms = []
    class BaseFormSet(StrAndUnicode): 
    97111        return form
    98112
    99113    def _get_initial_forms(self):
    100         """Return a list of all the intial forms in this formset."""
     114        """Return a list of all the initial forms in this formset."""
    101115        return self.forms[:self._initial_form_count]
    102116    initial_forms = property(_get_initial_forms)
    103117
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index d62a2ce..02780b6 100644
    a b def save_instance(form, instance, fields=None, fail_message='saved', 
    5454        # callable upload_to can use the values from other fields.
    5555        if isinstance(f, models.FileField):
    5656            file_field_list.append(f)
     57        # OneToOneField doesn't allow assignment of None. Guard against that
     58        # instead of allowing it and throwing an error.
     59        if isinstance(f, models.OneToOneField) and cleaned_data[f.name] is None:
     60            pass
    5761        else:
    5862            f.save_form_data(instance, cleaned_data[f.name])
    5963
    class BaseModelForm(BaseForm): 
    266270
    267271            lookup_kwargs = {}
    268272            for field_name in unique_check:
    269                 lookup_kwargs[field_name] = self.cleaned_data[field_name]
     273                lookup_value = self.cleaned_data[field_name]
     274                # ModelChoiceField will return an object instance rather than
     275                # a raw primary key value, so convert it to a pk value before
     276                # using it in a lookup.
     277                if isinstance(self.fields[field_name], ModelChoiceField):
     278                    lookup_value =  lookup_value.pk
     279                lookup_kwargs[field_name] = lookup_value
    270280
    271281            qs = self.instance.__class__._default_manager.filter(**lookup_kwargs)
    272282
    class BaseModelFormSet(BaseFormSet): 
    357367                 queryset=None, **kwargs):
    358368        self.queryset = queryset
    359369        defaults = {'data': data, 'files': files, 'auto_id': auto_id, 'prefix': prefix}
    360         defaults['initial'] = [model_to_dict(obj) for obj in self.get_queryset()]
    361370        defaults.update(kwargs)
    362371        super(BaseModelFormSet, self).__init__(**defaults)
    363372
     373    def _initial_forms(self):
     374        """Returns the number of forms that are required in this FormSet."""
     375        if not (self.data or self.files):
     376            return len(self.get_queryset())
     377        return super(BaseModelFormSet, self)._initial_forms()
     378    _initial_form_count = property(_initial_forms)
     379
    364380    def _construct_form(self, i, **kwargs):
    365381        if i < self._initial_form_count:
    366382            kwargs['instance'] = self.get_queryset()[i]
    class BaseModelFormSet(BaseFormSet): 
    380396
    381397    def save_new(self, form, commit=True):
    382398        """Saves and returns a new model instance for the given form."""
    383         return save_instance(form, self.model(), exclude=[self._pk_field.name], commit=commit)
     399        return form.save(commit=commit)
    384400
    385401    def save_existing(self, form, instance, commit=True):
    386402        """Saves and returns an existing model instance for the given form."""
    387         return save_instance(form, instance, exclude=[self._pk_field.name], commit=commit)
     403        return form.save(commit=commit)
    388404
    389405    def save(self, commit=True):
    390406        """Saves model instances for every form, adding and changing instances
    class BaseModelFormSet(BaseFormSet): 
    410426            existing_objects[obj.pk] = obj
    411427        saved_instances = []
    412428        for form in self.initial_forms:
    413             obj = existing_objects[form.cleaned_data[self._pk_field.name]]
     429            obj = existing_objects[form.cleaned_data[self._pk_field.name].pk]
    414430            if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
    415431                self.deleted_objects.append(obj)
    416432                obj.delete()
    class BaseModelFormSet(BaseFormSet): 
    438454
    439455    def add_fields(self, form, index):
    440456        """Add a hidden field for the object's primary key."""
    441         from django.db.models import AutoField
     457        from django.db.models import AutoField, OneToOneField, ForeignKey
    442458        self._pk_field = pk = self.model._meta.pk
    443459        if pk.auto_created or isinstance(pk, AutoField):
    444             form.fields[self._pk_field.name] = IntegerField(required=False, widget=HiddenInput)
     460            # The pk field we're adding to the form won't be able to get its
     461            # value from the ModelForm's instance argument, so provide an
     462            # initial value here.
     463            try:
     464                pk_value = self.get_queryset()[index].pk
     465            except IndexError:
     466                pk_value = None
     467            if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):
     468                qs = pk.rel.to._default_manager.get_query_set()
     469            else:
     470                qs = self.model._default_manager.get_query_set()
     471            form.fields[self._pk_field.name] = ModelChoiceField(qs, initial=pk_value, required=False, widget=HiddenInput)
    445472        super(BaseModelFormSet, self).add_fields(form, index)
    446473
    447474def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
    class BaseInlineFormSet(BaseModelFormSet): 
    477504        super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
    478505                                                queryset=qs)
    479506
    480     def _construct_forms(self):
     507    def _initial_forms(self):
    481508        if self.save_as_new:
    482             self._total_form_count = self._initial_form_count
    483             self._initial_form_count = 0
    484         super(BaseInlineFormSet, self)._construct_forms()
     509            return 0
     510        return super(BaseInlineFormSet, self)._initial_forms()
     511    _initial_form_count = property(_initial_forms)
     512
     513    def _total_forms(self):
     514        if self.save_as_new:
     515            return super(BaseInlineFormSet, self)._initial_forms()
     516        return super(BaseInlineFormSet, self)._total_forms()
     517    _total_form_count = property(_total_forms)
    485518
    486519    def _construct_form(self, i, **kwargs):
    487520        form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs)
    class BaseInlineFormSet(BaseModelFormSet): 
    498531    get_default_prefix = classmethod(get_default_prefix)
    499532
    500533    def save_new(self, form, commit=True):
    501         fk_attname = self.fk.get_attname()
    502         kwargs = {fk_attname: self.instance.pk}
    503         new_obj = self.model(**kwargs)
    504         if fk_attname == self._pk_field.attname or self._pk_field.auto_created:
    505             exclude =  [self._pk_field.name]
    506         else:
    507             exclude = []
    508         return save_instance(form, new_obj, exclude=exclude, commit=commit)
     534        # Use commit=False so we can assign the parent key afterwards, then
     535        # save the object.
     536        obj = form.save(commit=False)
     537        setattr(obj, self.fk.get_attname(), self.instance.pk)
     538        obj.save()
     539        # form.save_m2m() can be called via the formset later on if commit=False
     540        if commit and hasattr(form, 'save_m2m'):
     541            form.save_m2m()
     542        return obj
    509543
    510544    def add_fields(self, form, index):
    511545        super(BaseInlineFormSet, self).add_fields(form, index)
    class InlineForeignKeyField(Field): 
    620654        # ensure the we compare the values as equal types.
    621655        if force_unicode(value) != force_unicode(self.parent_instance.pk):
    622656            raise ValidationError(self.error_messages['invalid_choice'])
    623         if self.pk_field:
    624             return self.parent_instance.pk
    625657        return self.parent_instance
    626658
    627659class ModelChoiceIterator(object):
  • tests/modeltests/model_formsets/models.py

    diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
    index 0503ea2..d8cbe34 100644
    a b  
    1 
    21import datetime
    3 
    42from django import forms
    53from django.db import models
    64
    class Book(models.Model): 
    2725
    2826    def __unicode__(self):
    2927        return self.title
    30    
     28
    3129class BookWithCustomPK(models.Model):
    3230    my_pk = models.DecimalField(max_digits=5, decimal_places=0, primary_key=True)
    3331    author = models.ForeignKey(Author)
    class BookWithCustomPK(models.Model): 
    3533
    3634    def __unicode__(self):
    3735        return u'%s: %s' % (self.my_pk, self.title)
    38    
     36
    3937class AlternateBook(Book):
    4038    notes = models.CharField(max_length=100)
    41    
     39
    4240    def __unicode__(self):
    4341        return u'%s - %s' % (self.title, self.notes)
    44    
     42
    4543class AuthorMeeting(models.Model):
    4644    name = models.CharField(max_length=100)
    4745    authors = models.ManyToManyField(Author)
    class Owner(models.Model): 
    6866    auto_id = models.AutoField(primary_key=True)
    6967    name = models.CharField(max_length=100)
    7068    place = models.ForeignKey(Place)
    71    
     69
    7270    def __unicode__(self):
    7371        return "%s at %s" % (self.name, self.place)
    7472
    class Location(models.Model): 
    8179class OwnerProfile(models.Model):
    8280    owner = models.OneToOneField(Owner, primary_key=True)
    8381    age = models.PositiveIntegerField()
    84    
     82
    8583    def __unicode__(self):
    8684        return "%s is %d" % (self.owner.name, self.age)
    8785
    class MexicanRestaurant(Restaurant): 
    114112# using inlineformset_factory.
    115113class Repository(models.Model):
    116114    name = models.CharField(max_length=25)
    117    
     115
    118116    def __unicode__(self):
    119117        return self.name
    120118
    121119class Revision(models.Model):
    122120    repository = models.ForeignKey(Repository)
    123121    revision = models.CharField(max_length=40)
    124    
     122
    125123    class Meta:
    126124        unique_together = (("repository", "revision"),)
    127    
     125
    128126    def __unicode__(self):
    129127        return u"%s (%s)" % (self.revision, unicode(self.repository))
    130128
    class Team(models.Model): 
    146144class Player(models.Model):
    147145    team = models.ForeignKey(Team, null=True)
    148146    name = models.CharField(max_length=100)
    149    
     147
     148    def __unicode__(self):
     149        return self.name
     150
     151# Models for testing custom ModelForm save methods in formsets and inline formsets
     152class Poet(models.Model):
     153    name = models.CharField(max_length=100)
     154
     155    def __unicode__(self):
     156        return self.name
     157
     158class Poem(models.Model):
     159    poet = models.ForeignKey(Poet)
     160    name = models.CharField(max_length=100)
     161
    150162    def __unicode__(self):
    151163        return self.name
    152164
    used. 
    337349
    338350>>> AuthorFormSet = modelformset_factory(Author, max_num=2)
    339351>>> formset = AuthorFormSet(queryset=qs)
    340 >>> [sorted(x.items()) for x in formset.initial]
    341 [[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')]]
     352>>> [x.name for x in formset.get_queryset()]
     353[u'Charles Baudelaire', u'Paul Verlaine']
    342354
    343355>>> AuthorFormSet = modelformset_factory(Author, max_num=3)
    344356>>> formset = AuthorFormSet(queryset=qs)
    345 >>> [sorted(x.items()) for x in formset.initial]
    346 [[('id', 1), ('name', u'Charles Baudelaire')], [('id', 3), ('name', u'Paul Verlaine')], [('id', 2), ('name', u'Walt Whitman')]]
     357>>> [x.name for x in formset.get_queryset()]
     358[u'Charles Baudelaire', u'Paul Verlaine', u'Walt Whitman']
     359
     360
     361# ModelForm with a custom save method in a formset ###########################
     362
     363>>> class PoetForm(forms.ModelForm):
     364...     def save(self, commit=True):
     365...         # change the name to "Vladimir Mayakovsky" just to be a jerk.
     366...         author = super(PoetForm, self).save(commit=False)
     367...         author.name = u"Vladimir Mayakovsky"
     368...         if commit:
     369...             author.save()
     370...         return author
     371
     372>>> PoetFormSet = modelformset_factory(Poet, form=PoetForm)
     373
     374>>> data = {
     375...     'form-TOTAL_FORMS': '3', # the number of forms rendered
     376...     'form-INITIAL_FORMS': '0', # the number of forms with initial data
     377...     'form-0-name': 'Walt Whitman',
     378...     'form-1-name': 'Charles Baudelaire',
     379...     'form-2-name': '',
     380... }
     381
     382>>> qs = Poet.objects.all()
     383>>> formset = PoetFormSet(data=data, queryset=qs)
     384>>> formset.is_valid()
     385True
     386
     387>>> formset.save()
     388[<Poet: Vladimir Mayakovsky>, <Poet: Vladimir Mayakovsky>]
     389
    347390
    348391# Model inheritance in model formsets ########################################
    349392
    True 
    553596[<AlternateBook: Flowers of Evil - English translation of Les Fleurs du Mal>]
    554597
    555598
     599# ModelForm with a custom save method in an inline formset ###################
     600
     601>>> class PoemForm(forms.ModelForm):
     602...     def save(self, commit=True):
     603...         # change the name to "Brooklyn Bridge" just to be a jerk.
     604...         poem = super(PoemForm, self).save(commit=False)
     605...         poem.name = u"Brooklyn Bridge"
     606...         if commit:
     607...             poem.save()
     608...         return poem
     609
     610>>> PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm)
     611
     612>>> data = {
     613...     'poem_set-TOTAL_FORMS': '3', # the number of forms rendered
     614...     'poem_set-INITIAL_FORMS': '0', # the number of forms with initial data
     615...     'poem_set-0-name': 'The Cloud in Trousers',
     616...     'poem_set-1-name': 'I',
     617...     'poem_set-2-name': '',
     618... }
     619
     620>>> poet = Poet.objects.create(name='Vladimir Mayakovsky')
     621>>> formset = PoemFormSet(data=data, instance=poet)
     622>>> formset.is_valid()
     623True
     624
     625>>> formset.save()
     626[<Poem: Brooklyn Bridge>, <Poem: Brooklyn Bridge>]
     627
     628
    556629# Test a custom primary key ###################################################
    557630
    558631We need to ensure that it is displayed
Back to Top