I followed the flush process and I think for now it's hard to trick the 
persistence to make it thinks it has to emit an update statement for 
"entity" table, only for columns having an onupdate attribute (and a value 
computed from this definition).

So I can only make an ad-hoc workaround in "before_flush":

@sa.event.listens_for(sa.orm.Session, 'before_flush')
def polymorphic_onupdate(session, flush_context, instances):
    for obj in session.dirty:
        if not isinstance(obj, Parent):
            continue
        state = sa.inspect(obj)
        history = state.get_history('updated_at', state.dict)
        # don't overwrite if value has been set "manually" in this 
transaction
        if not any((history.added, history.deleted)):
            obj.updated_at = datetime.utcnow()

If Parent has other auto-update columns they will be included in the update 
statement.

Anyway If I have the reverse problem (Child has a column with onupdate) I 
would have to ensure that child table is updated in the case where only 
Parent attributes have been modified. And so on if I have a class 
OtherChild(Parent), etc.

Le vendredi 8 novembre 2013 13:28:59 UTC+1, Richard Gerd Kuesters a écrit :
>
>  Yup, I understood your question :) Let me see if I can be a little more 
> specific.
>
> Even with inheritance and polymorphism, you still have two tables (entity 
> and child) in the database. If you update just the value in the Child 
> instance (which exists just in the child table), the entity table has 
> nothing to do with it. If you "touch" only your Child instance attribute 
> and none from the Parent instance, only its value (child) will be updated.
>
> You can check if your parent instance is dirty after changing a value from 
> child, it will probably return False:
>
>
> *obj.value = "foobar"*
> *p = session.query(Parent).filter(Parent.id == obj.id 
> <http://obj.id>).one()*
> *p in session.dirty  # should return False. If returns True, then I think 
> it is a bug*
> *obj in session.dirty # shoud return True. go figure ...*
>
>
> *IF* you want this to work without much effort, you should use updated_at 
> in Child (which I agree if you think "you must be joking" or "you really 
> must be joking", lol). If don't, polymorphism will not do that 
> "automagically", at least IMHO. You can also check if there's a way to make 
> the whole object dirty :)
>
> Indeed it's a tricky situation, SA is expected by us to do this (and I 
> think it may be our fault), but if it doesn't, the logic is also plausible.
>
> I'll dig around the code too see what can be done.
>
>
> Cheers,
> Richard.
>
>
> On 11/08/2013 09:34 AM, Bertrand Mathieu wrote:
>  
> Hello,
>
> Thank you for your answer, but unless I miss something in your answer my 
> problem has to do with polymorphic inheritance, it's not about relationship 
> between 2 models. By inheritance "updated_at" is an attribute of any Child 
> instance ( Child().updated_at exists).
>
> I realize I didn't explained the example I joined:
>
> (I also realize that Parent.__tablename__ is named 'entity', don't be 
> confused)
>
> class definitions:
> class Parent(Base):
>     __tablename__ = 'entity'
>     __mapper_args__ = {'polymorphic_on': 'entity_type',
>                       'with_polymorphic': '*',
>                        }
>     entity_type =  Column('entity_type', String(1000), nullable=True)
>
>     id = Column('id', Integer(), primary_key=True)
>     name =  Column('name', String(1000), default=u'')
>     updated_at = Column(sa.DateTime,
>                         default=datetime.utcnow,
>                         onupdate=datetime.utcnow)
>
>
> class Child(Parent):
>     __tablename__ = 'child'
>     __mapper_args__ = {'polymorphic_identity': 'Child'}
>     id = Column(Integer,
>                 ForeignKey('entity.id', name='fk_inherited_entity_id'),
>                 primary_key=True)
>
>     text = Column('text', String(1000), default=u'')
>  
> When run() is executed:
>
>      obj = Child()
>     session.add(obj)
>     session.commit()
>
> SQL emitted:
> INSERT INTO entity (entity_type, name, updated_at) VALUES (?, ?, ?)
> ('Child', u'', '2013-11-08 10:27:18.886677')
> INSERT INTO child (id, text) VALUES (?, ?)
> (1, u'')
> COMMIT 
>  
> I change only one of Child's attributes:
>         obj.text = 'some text'
>     session.commit()
>
> SQL:
> UPDATE child SET text=? WHERE child.id = ?
> ('some text', 1)
> COMMIT
>  
> -> updated_at on table entity is not changed, so obj.updated_at is left 
> untouched: don't want that.
>
> But if I also change "name" (defined on Parent)
>     obj.name = u'test'
>     obj.text = 'some text'
>     session.commit()
>
> SQL:
>  UPDATE entity SET name=?, updated_at=? WHERE entity.id = ?
> (u'test', '2013-11-08 10:27:18.891200', 1)
> UPDATE child SET text=? WHERE child.id = ?
> ('some text', 1)
> COMMIT
>  
> obj.updated_at is changed.
>
>
> Le vendredi 8 novembre 2013 12:09:13 UTC+1, Richard Gerd Kuesters a 
> écrit : 
>>
>>  I think the code is correct and is behaving correctly also -- since you 
>> update just the child, the parent isn't touched (it already has data and 
>> was not changed in this session).
>>
>> What you could do is add an update event to your Child object:
>>
>>
>> *def update_child(mapper, connection, target):*
>> *    # target is your child updated*
>> *    target.parent.updated_at = datetime.datetime.now()  # assuming you 
>> have mapped parent as a backref object or something*
>> *    object_session(target).commit()*
>>
>> *event.listen(Child, 'after_update',  update_child)*
>>
>>
>> If you want the same after an insert or delete, you should declare other 
>> events[1] as well. I think there might be other ways to do that, but I 
>> usually use events for its flexibility.
>>
>> You can also use before_update to use the same session.commit()
>>
>> [1] http://docs.sqlalchemy.org/en/rel_0_8/orm/events.html
>>
>> Best regards,
>> Richard.
>>
>>  On 11/08/2013 08:38 AM, Bertrand Mathieu wrote:
>>  
>> Hi,
>>
>> I have a set up class inheritance using joined table inheritance. I'm 
>> using sqlalchemy 0.8.2.
>>
>> The Parent class has a DateTime attribute "updated_at", with 
>> onupdate=datetime.utcnow.
>>
>> If I update only one of the Child's attributes, only "child" table is 
>> updated, parent.updated_at is not changed. If I change one of the Parent's 
>> attributes, then updated_at is updated as expected. 
>>
>> Here's my questions:
>> 1) Am I missing something in my setup? is it normal or is it a bug?
>> 2) If this is normal, what is the right way to tell session that 
>> "parent.updated_at" should be modified too?
>>
>> Regards,
>> -- 
>> Bertrand
>>
>>  -- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to sqlalchemy+...@googlegroups.com.
>> To post to this group, send email to sqlal...@googlegroups.com.
>> Visit this group at http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>>
>>   -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlalchemy+...@googlegroups.com <javascript:>.
> To post to this group, send email to sqlal...@googlegroups.com<javascript:>
> .
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
> 

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to