So you need to instead be looking at the DDL that's being emitted and not the "SHOW CREATE TABLE" which I assume is what's above.
"server_onupdate" does *not* generate any DDL, as there is no such thing as "ON UPDATE" in SQL; this is a MySQL-specific extension that I believe only applies to their TIMESTAMP datatype in the first place.
Support for MySQL's "ON UPDATE" phrase is not included as a first class feature in SQLAlchemy right now and
https://github.com/sqlalchemy/sqlalchemy/issues/4652 seeks to add this functionality. However a widely used workaround is to apply the "ON UPDATE" inside the "server_default" field, which *is* part of standard SQL, e.g. server_default=text("DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP") or whatever you want it to be.
as for the nullable part, you've unfortunately found a bug, in that the TypeDecorator is preventing it from detecting the special case nullability for TIMESTAMP. It will be fixed in about 90 minutes
https://github.com/sqlalchemy/sqlalchemy/issues/4743 but a new SQLAlchemy release isn't for a couple of weeks most likely. For now what I would do is add an "ALTER TABLE" command in a textual way to your metadata, and you can make whatever result you'd like occur here:
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
data = DeletedAt()
event.listen(
A.__table__,
'after_create',
DDL(
"ALTER TABLE a MODIFY deleted_at TIMESTAMP NULL "
"DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP").execute_if(dialect="mysql")
)
the ALTER will fire off after the CREATE TABLE.