Django

Code

Changeset 7507

Show
Ignore:
Timestamp:
04/29/08 00:45:46 (1 year ago)
Author:
brosner
Message:

newforms-admin: Fixed #6117 -- Implemented change history for the admin. This includes the ability to track changes on a newform. Model formsets now only return the changed/new objects saved. A big thanks to Karen Tracey and Alex Gaynor.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/newforms-admin/django/contrib/admin/options.py

    r7484 r7507  
    390390                formset.save() 
    391391 
    392         # Construct the change message. TODO: Temporarily commented-out, 
    393         # as manipulator object doesn't exist anymore, and we don't yet 
    394         # have a way to get fields_added, fields_changed, fields_deleted. 
     392        # Construct the change message.                  
    395393        change_message = [] 
    396         #if manipulator.fields_added: 
    397             #change_message.append(_('Added %s.') % get_text_list(manipulator.fields_added, _('and'))) 
    398         #if manipulator.fields_changed: 
    399             #change_message.append(_('Changed %s.') % get_text_list(manipulator.fields_changed, _('and'))) 
    400         #if manipulator.fields_deleted: 
    401             #change_message.append(_('Deleted %s.') % get_text_list(manipulator.fields_deleted, _('and'))) 
    402         #change_message = ' '.join(change_message) 
     394        if form.changed_data: 
     395            change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and'))) 
     396             
     397        if formsets: 
     398            for formset in formsets: 
     399                for added_object in formset.new_objects: 
     400                    change_message.append(_('Added %s "%s".')  
     401                                          % (added_object._meta.verbose_name, added_object)) 
     402                for changed_object, changed_fields in formset.changed_objects: 
     403                    change_message.append(_('Changed %s for %s "%s".')  
     404                                          % (get_text_list(changed_fields, _('and')),  
     405                                             changed_object._meta.verbose_name,  
     406                                             changed_object)) 
     407                for deleted_object in formset.deleted_objects: 
     408                    change_message.append(_('Deleted %s "%s".')  
     409                                          % (deleted_object._meta.verbose_name, deleted_object)) 
     410             
     411        change_message = ' '.join(change_message) 
    403412        if not change_message: 
    404413            change_message = _('No fields changed.') 
  • django/branches/newforms-admin/django/newforms/forms.py

    r7366 r7507  
    8282        self.empty_permitted = empty_permitted 
    8383        self._errors = None # Stores the errors after clean() has been called. 
     84        self._changed_data = None 
    8485 
    8586        # The base_fields class attribute is the *class-wide* definition of 
     
    244245        Returns True if data differs from initial. 
    245246        """ 
    246         # XXX: For now we're asking the individual widgets whether or not the 
    247         # data has changed. It would probably be more efficient to hash the 
    248         # initial data, store it in a hidden field, and compare a hash of the 
    249         # submitted data, but we'd need a way to easily get the string value 
    250         # for a given field. Right now, that logic is embedded in the render 
    251         # method of each widget. 
    252         for name, field in self.fields.items(): 
    253             prefixed_name = self.add_prefix(name) 
    254             data_value = field.widget.value_from_datadict(self.data, self.files, prefixed_name) 
    255             initial_value = self.initial.get(name, field.initial) 
    256             if field.widget._has_changed(initial_value, data_value): 
    257                 return True 
    258         return False 
     247        return bool(self.changed_data) 
     248     
     249    def _get_changed_data(self): 
     250        if self._changed_data is None: 
     251            self._changed_data = [] 
     252            # XXX: For now we're asking the individual widgets whether or not the 
     253            # data has changed. It would probably be more efficient to hash the 
     254            # initial data, store it in a hidden field, and compare a hash of the 
     255            # submitted data, but we'd need a way to easily get the string value 
     256            # for a given field. Right now, that logic is embedded in the render 
     257            # method of each widget. 
     258            for name, field in self.fields.items(): 
     259                prefixed_name = self.add_prefix(name) 
     260                data_value = field.widget.value_from_datadict(self.data, self.files, prefixed_name) 
     261                initial_value = self.initial.get(name, field.initial) 
     262                if field.widget._has_changed(initial_value, data_value): 
     263                    self._changed_data.append(name) 
     264        return self._changed_data 
     265    changed_data = property(_get_changed_data) 
    259266 
    260267    def _get_media(self): 
  • django/branches/newforms-admin/django/newforms/models.py

    r7365 r7507  
    329329 
    330330    def save_existing_objects(self, commit=True): 
     331        self.changed_objects = [] 
     332        self.deleted_objects = [] 
    331333        if not self.get_queryset(): 
    332334            return [] 
     335 
    333336        # Put the objects from self.get_queryset into a dict so they are easy to lookup by pk 
    334337        existing_objects = {} 
     
    339342            obj = existing_objects[form.cleaned_data[self.model._meta.pk.attname]] 
    340343            if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: 
     344                self.deleted_objects.append(obj) 
    341345                obj.delete() 
    342346            else: 
    343                 saved_instances.append(self.save_existing(form, obj, commit=commit)) 
     347                if form.changed_data: 
     348                    self.changed_objects.append((obj, form.changed_data)) 
     349                    saved_instances.append(self.save_existing(form, obj, commit=commit)) 
    344350        return saved_instances 
    345351 
    346352    def save_new_objects(self, commit=True): 
    347         new_objects = [] 
     353        self.new_objects = [] 
    348354        for form in self.extra_forms: 
    349355            if not form.has_changed(): 
    350356                continue 
    351357            # If someone has marked an add form for deletion, don't save the 
    352             # object. At some point it would be nice if we didn't display 
    353             # the deletion widget for add forms. 
     358            # object. 
    354359            if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]: 
    355360                continue 
    356             new_objects.append(self.save_new(form, commit=commit)) 
    357         return new_objects 
     361            self.new_objects.append(self.save_new(form, commit=commit)) 
     362        return self.new_objects 
    358363 
    359364    def add_fields(self, form, index): 
  • django/branches/newforms-admin/tests/modeltests/model_formsets/models.py

    r7270 r7507  
    5151 
    5252Gah! We forgot Paul Verlaine. Let's create a formset to edit the existing 
    53 authors with an extra form to add him. This time we'll use formset_for_queryset. 
    54 We *could* use formset_for_queryset to restrict the Author objects we edit, 
    55 but in that case we'll use it to display them in alphabetical order by name. 
     53authors with an extra form to add him. We *could* pass in a queryset to 
     54restrict the Author objects we edit, but in this case we'll use it to display 
     55them in alphabetical order by name. 
    5656 
    5757>>> qs = Author.objects.order_by('name') 
     
    8080True 
    8181 
    82 >>> formset.save() 
    83 [<Author: Arthur Rimbaud>, <Author: Charles Baudelaire>, <Author: Paul Verlaine>] 
     82# Only changed or new objects are returned from formset.save() 
     83>>> formset.save() 
     84[<Author: Paul Verlaine>] 
    8485 
    8586>>> for author in Author.objects.order_by('name'): 
     
    125126True 
    126127 
    127 >>> formset.save() 
    128 [<Author: Arthur Rimbaud>, <Author: Charles Baudelaire>, <Author: Paul Verlaine>] 
     128# No objects were changed or saved so nothing will come back. 
     129>>> formset.save() 
     130[] 
    129131 
    130132>>> for author in Author.objects.order_by('name'): 
     
    134136Paul Verlaine 
    135137 
     138Let's edit a record to ensure save only returns that one record. 
     139 
     140>>> data = { 
     141...     'form-TOTAL_FORMS': '4', # the number of forms rendered 
     142...     'form-INITIAL_FORMS': '3', # the number of forms with initial data 
     143...     'form-0-id': '2', 
     144...     'form-0-name': 'Walt Whitman', 
     145...     'form-1-id': '1', 
     146...     'form-1-name': 'Charles Baudelaire', 
     147...     'form-2-id': '3', 
     148...     'form-2-name': 'Paul Verlaine', 
     149...     'form-3-name': '', 
     150...     'form-3-DELETE': '', 
     151... } 
     152 
     153>>> formset = AuthorFormSet(data=data, queryset=qs) 
     154>>> formset.is_valid() 
     155True 
     156 
     157# One record has changed. 
     158>>> formset.save() 
     159[<Author: Walt Whitman>] 
    136160 
    137161# Inline Formsets ############################################################ 
     
    200224 
    201225>>> formset.save() 
    202 [<Book: Les Fleurs du Mal>, <Book: Le Spleen de Paris>] 
     226[<Book: Le Spleen de Paris>] 
    203227 
    204228As you can see, 'Le Spleen de Paris' is now a book belonging to Charles Baudelaire.