Hi,

Since you have already done what you required, I'm not going much into
detail, but your process basically looks pretty lengthy.

Normally, to do these sort of stuff I only override two ModelAdmin methods:
- save_model()
- save_formset()

Using them together normally serves any kind of purpose, and the code looks
cleaner too. Maybe you can try with experimenting them out sometime.

Thanks,
Subhranath Chunder.

On Mon, Sep 27, 2010 at 8:19 AM, jonathan.morgan <
jonathan.morgan....@gmail.com> wrote:

> Update:
>
> Well, I cobbled together a few things to accomplish most of what I
> needed to accomplish, and figured I'd write it down here so I can look
> it up later and so others can learn from it (the doc on m2m_changed,
> in particular, is not incomplete, but not detailed or particularly
> precise, and I had trouble finding a good example).
>
> The event to event_date relation was one-to-many, so there is a model
> for event dates that contains the key to each date's related event.
> For these, I overrode the save and delete methods for the event_dates
> model so it notifies the related event when any related date is added,
> changed, or deleted, after the actual save on insert, update, or
> delete, so the related event (which is still present in the instance
> even on delete, though the relation no longer exists in the database)
> can invoke a method that accepts the date and checks if it potentially
> needs to re-calculate its overall start and end dates from the
> database based on where the date lies in relation to its current start
> and end dates.  Part of the problem I was running into is that the
> internal set of event_dates for an event were not getting updated as
> updates from the admin screen were getting processed.  The database is
> updated, though, and so the event can query the database for updated
> dates and update its start and end date accurately even though its
> internal Set representation of associated dates gets out of sync.
>
> For the ManyToMany, I ended up being able to use the m2m_changed
> signal combined with overriding the save and delete on the right-hand
> side of the m2m relationship to let the event know that something had
> changed, and so it should rebuild its cached list of series and
> providers.  First, a few notes on what precisely the m2m_changed
> signal passes:
>
> - sender: Python new-class Type instance for the django-generated
> "through" class that connects the two models (Event.presenters.through
> and Event.series.through in this case).
>
> - instance: an instance of the model that contains the ManyToManyField
> relationship (Event, in this case).
>
> - model: Python new-class Type instance for the class of the model
> that is on the right side of the M2M relationship, joined by the
> "through" class that is in the "sender" parameter (Series and
> Presenter model classes in the two functions below).
>
> - action - either "pre_add"/"post_add" or
> "pre_clear"/"post_clear" ("pre_remove" and "post_remove" are never
> sent by the admin, as far as I can tell, since it just clears all
> relations each time a record is saved, then adds in relations again
> that still are present - not perhaps how I would have done it, but
> efficient).
>
> Because of how django admin implements processing changes to m2m
> relations (clear all, then re-add all that are present in form submit)
> m2m_changed doesn't actually tell you how the relation changed (so no
> signals for things getting removed, and no good signals on "add"
> versus "still here"), so I had to just use these signals to let the
> event know something had changed with a given set of relations, and
> have it call a method to update itself from the database to figure out
> what had changed and take the changes into account.  This worked, but
> wasn't quite as targeted as it could be if admin actually passed
> m2m_changed signals for removal, and didn't pass add signals for
> relations that were unchanged.
>
> I also limited the event listeners to only the "through" senders I
> cared about (see code sample below for accessing a "through" class -
> import the class that contains the ManyToManyField, then reference
> "class_name"."m2m_field_name".through).  Here is the code I ended up
> with for registering the signals (in my models.py file, so the Event
> class is already there, doesn't need to be imported, and including the
> debug and logging code I used to figure much of this out):
>
> # imports for handling signals that ManyToMany relations have changed.
> from django.db.models.signals import m2m_changed
> from django.dispatch import receiver
>
> @receiver( m2m_changed, sender = Event.series.through )
> def process_event_series_updates( sender, **kwargs_IN ):
>
>    # declare variables
>    instance_IN = None #-- kwargs_IN[ "instance" ]
>    action_IN = '' #-- kwargs_IN[ "action" ] - from
> http://docs.djangoproject.com/en/dev/ref/signals/, will be either
> "pre_add", "post_add", "pre_remove", "post_remove", "pre_clear", or
> "post_clear"
>    reverse_IN = '' #-- kwargs_IN[ "reverse" ]
>    model_IN = None #-- kwargs_IN[ "model" ]
>    pk_set_IN = None #-- kwargs_IN[ "pk_set" ]
>    using_IN = None #-- kwargs_IN[ "using" ]
>    instance_IN = None #-- kwargs_IN[ instance ]
>
>    # just try reading the arguments, outputting a log message.
>    instance_IN = kwargs_IN[ "instance" ]
>    action_IN = kwargs_IN[ "action" ]
>    reverse_IN = kwargs_IN[ "reverse" ]
>    model_IN = kwargs_IN[ "model" ]
>    pk_set_IN = kwargs_IN[ "pk_set" ]
>    using_IN = kwargs_IN[ "using" ]
>
>    #logging.debug( "In process_event_series_updates(): We have a
> signal. sender = " + str( sender ) + "; action = " + action_IN + ";
> reverse = " + str( reverse_IN ) + "; model = " + str( model_IN ) + ";
> pk_set = " + str( pk_set_IN ) +  "; using = " + using_IN + ";
> instance? = " + str( instance_IN ) + " (class: " +
> str( instance_IN.__class__ ) + ")." )
>
>    # see if we are a "post_add" or "post_remove" action.
>    if ( ( action_IN == "post_add" ) or ( action_IN ==
> "post_remove" ) ):
>
>        # we are a post-add.  Rebuild the series field, then save.
>        instance_IN.update_series_name()
>        instance_IN.save()
>
>    #-- END check to see if we are post-add. --#
>
> #-- end function process_event_series_updates() --#
>
>
> @receiver( m2m_changed, sender = Event.presenters.through )
> def process_event_presenter_updates( sender, **kwargs_IN ):
>
>    # declare variables
>    instance_IN = None #-- kwargs_IN[ "instance" ]
>    action_IN = '' #-- kwargs_IN[ "action" ] - from
> http://docs.djangoproject.com/en/dev/ref/signals/, will be either
> "pre_add", "post_add", "pre_remove", "post_remove", "pre_clear", or
> "post_clear"
>    reverse_IN = '' #-- kwargs_IN[ "reverse" ]
>    model_IN = None #-- kwargs_IN[ "model" ]
>    pk_set_IN = None #-- kwargs_IN[ "pk_set" ]
>    using_IN = None #-- kwargs_IN[ "using" ]
>    instance_IN = None #-- kwargs_IN[ instance ]
>
>    # just try reading the arguments, outputting a log message.
>    instance_IN = kwargs_IN[ "instance" ]
>    action_IN = kwargs_IN[ "action" ]
>    reverse_IN = kwargs_IN[ "reverse" ]
>    model_IN = kwargs_IN[ "model" ]
>    pk_set_IN = kwargs_IN[ "pk_set" ]
>    using_IN = kwargs_IN[ "using" ]
>
>    #logging.debug( "In process_event_presenter_updates(): We have a
> signal. sender = " + str( sender ) + "; action = " + action_IN + ";
> reverse = " + str( reverse_IN ) + "; model = " + str( model_IN ) + ";
> pk_set = " + str( pk_set_IN ) +  "; using = " + using_IN + ";
> instance? = " + str( instance_IN ) + " (class: " +
> str( instance_IN.__class__ ) + ")." )
>
>    # see if we are a "post_add"  or "post_remove" action.
>    if ( ( action_IN == "post_add" ) or ( action_IN ==
> "post_remove" ) ):
>
>        # we are a post-add.  Rebuild the series field, then save.
>        instance_IN.update_presenter()
>        instance_IN.save()
>
>    #-- END check to see if we are post-add. --#
>
> #-- end function process_event_presenter_updates() --#
>
>
> On Sep 18, 3:00 pm, "jonathan.morgan" <jonathan.morgan....@gmail.com>
> wrote:
> > One clarification:  I think I read somewhere that django handles
> > transactions on a per-request basis.  So, there probably isn't a post-
> > commit hook.  If the instances were kept up to date, though, there
> > could be a post-all-updates hook...
> >
> > On Sep 18, 12:54 pm, "jonathan.morgan" <jonathan.morgan....@gmail.com>
> > wrote:
> >
> > > Hello,
> >
> > > In brief, does anyone know of a way to hook code into the save in the
> > > admin so that it executes after the main model and any relations
> > > created because of inlines are committed to the database?  I don't
> > > necessarily need it to give me a re-loaded model instance, but I would
> > > at least like to be able to get at a model instance that reflects the
> > > state of the database post-save so I can update fields in the parent
> > > model based on the current state of its relations.
> >
> > > I am using the django admin code from the trunk version of django (1.3
> > > pre-alpha SVN-13861).
> >
> > > In my models, I have an event model that can be connected to a number
> > > of different related objects:
> > > - event dates (which hold start and end date, start and end time, and
> > > notes particular to each performance, such as understudies performing)
> > > - event presenters (a ManyToMany relationship)
> > > - event series (a ManyToMany relationship)
> >
> > > After adding or removing dates, presenters, or series, I would like to
> > > be able to call a method that looks at all the related objects for an
> > > event and places values in a few fields on the event:
> > > - figures out the start and end date for the event based on all event
> > > dates and places them in datetime fields in the event model.
> > > - creates a string of all the presenter names, separated by semi-
> > > colons, and places that in a string field.
> > > - creates a string of all the series names, separated by semi-colons,
> > > and places that in a string field.
> >
> > > I am trying to get information from these relationships back into the
> > > base model so they can be used in the admin, for searching and
> > > sorting.
> >
> > > Here is what I tried so far:
> > > - I tried the save_model() hook in the admin class for event, but the
> > > relationships have not yet been added to the event at this point (or
> > > to the database - I tried querying the database to pull in all of the
> > > event_date table rows for the event's ID, for example, and nothing was
> > > returned).
> > > - I tried overriding the save() method on the event so it rebuilds the
> > > cached fields directly from the database.  Since this is at the same
> > > point in the admin save process as save_model(), this also did not
> > > work.
> > > - I tried making my own join model for event to presenter (as a test),
> > > then overrode its save() method to rebuild its associated event's
> > > cached presenter string on update.  No dice here either.
> > > - I tried reloading the event using it's ID at each of these points,
> > > to see if the database had been updated but the model instance needed
> > > to be reloaded to reflect changes.  This didn't work in any of the
> > > three places above, indicating to me at least that the updates to the
> > > main model and inline models are combined into a transaction that is
> > > only committed after everything is validated and there are no errors.
> >
> > > So it looks like all the updates to the main model and inline models
> > > are combined into a transaction, and there is no easy way to insert
> > > code after that transaction is committed.
> >
> > > Does anyone know of a way to hook code into the save in the admin so
> > > that it executes after the main model and any relations created
> > > because of inlines are at the least committed to the database?  I
> > > don't necessarily need it to give me a re-loaded model instance, but I
> > > would at least like to be able to get at a model instance that
> > > reflects the state of the database post-save.
> >
> > > I am hoping I am missing something basic, but I've searched the
> > > internet, this group, and the django issue-tracking system and have
> > > not found anything.  I'll appreciate any help anyone can provide.
> >
> > > Thanks,
> >
> > > Jonathan Morgan
> >
> >
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.
> To post to this group, send email to django-us...@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users+unsubscr...@googlegroups.com<django-users%2bunsubscr...@googlegroups.com>
> .
> For more options, visit this group at
> http://groups.google.com/group/django-users?hl=en.
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-us...@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to