SQLAlchemy default DateTime
Solution 1:
Calculate timestamps within your DB, not your client
For sanity, you probably want to have all datetimes
calculated by your DB server, rather than the application server. Calculating the timestamp in the application can lead to problems because network latency is variable, clients experience slightly different clock drift, and different programming languages occasionally calculate time slightly differently.
SQLAlchemy allows you to do this by passing func.now()
or func.current_timestamp()
(they are aliases of each other) which tells the DB to calculate the timestamp itself.
Use SQLALchemy's server_default
Additionally, for a default where you're already telling the DB to calculate the value, it's generally better to use server_default
instead of default
. This tells SQLAlchemy to pass the default value as part of the CREATE TABLE
statement.
For example, if you write an ad hoc script against this table, using server_default
means you won't need to worry about manually adding a timestamp call to your script--the database will set it automatically.
Understanding SQLAlchemy's onupdate
/server_onupdate
SQLAlchemy also supports onupdate
so that anytime the row is updated it inserts a new timestamp. Again, best to tell the DB to calculate the timestamp itself:
from sqlalchemy.sql import func
time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())
There is a server_onupdate
parameter, but unlike server_default
, it doesn't actually set anything serverside. It just tells SQLalchemy that your database will change the column when an update happens (perhaps you created a trigger on the column ), so SQLAlchemy will ask for the return value so it can update the corresponding object.
One other potential gotcha:
You might be surprised to notice that if you make a bunch of changes within a single transaction, they all have the same timestamp. That's because the SQL standard specifies that CURRENT_TIMESTAMP
returns values based on the start of the transaction.
PostgreSQL provides the non-SQL-standard statement_timestamp()
and clock_timestamp()
which do change within a transaction. Docs here: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
UTC timestamp
If you want to use UTC timestamps, a stub of implementation for func.utcnow()
is provided in SQLAlchemy documentation. You need to provide appropriate driver-specific functions on your own though.
Solution 2:
DateTime
doesn't have a default key as an input. The default key should be an input to the Column
function. Try this:
import datetime
from sqlalchemy import Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
created_date = Column(DateTime, default=datetime.datetime.utcnow)
Solution 3:
You can also use sqlalchemy builtin function for default DateTime
from sqlalchemy.sql import func
DT = Column(DateTime(timezone=True), default=func.now())
Solution 4:
You likely want to use onupdate=datetime.now
so that UPDATEs also change the last_updated
field.
SQLAlchemy has two defaults for python executed functions.
-
default
sets the value on INSERT, only once -
onupdate
sets the value to the callable result on UPDATE as well.