问题
I'm in the process of migrating hosts, and the new hosts uses a newer version of MySQL which is stricter about the format of datetime values it accepts. I know that I can turn this off, but I'd like to find a solution to properly convert these values to the correct format. Preferably one that can be easily swapped out across the many models I need to update (100 or so).
If I can't find a way to update this on the model level, I may have to hunt down the thousands of locations where these values are set.
I've trimmed down an example model here:
class Timesheet(BaseModel, db.Model):
__tablename__ = "timesheet"
timesheet_id = db.Column(db.Integer, primary_key=True)
created = db.Column(db.DateTime, default=datetime.utcnow)
updated = db.Column(db.DateTime)
employee_id = db.Column(db.Integer, db.ForeignKey('user.employee_id'))
description = db.Column(db.String(255))
hours = db.Column(db.Numeric(9, 2))
billable = db.Column(db.Boolean, default=False)
retainer = db.Column(db.Boolean, default=False)
client_id = db.Column(db.Integer, db.ForeignKey('client.client_id'), default=None)
project_id = db.Column(db.Integer, db.ForeignKey('project.project_id'), default=None)
task_id = db.Column(db.Integer, default=None)
timesheet_date = db.Column(db.DateTime, default=datetime.utcnow)
I'm hoping there is a way to modify db.Datetime so that it returns a true Python datetime object when provided with an ISO8601 datetime string ('YYYY-MM-DDT00:00:00.000Z')
Looking for something along the lines of:
class FormattedDateTime(db.DateTime):
def self.__set__(self, value):
return dateutil.parser.parse(value)
The column definition would then be changed to:
updated = db.Column(FormattedDateTime)
...so that it will automatically save with the correct format when SQLAlchemy sends to MySQL.
I've looked at Mixins and searched around for approaches that will accomplish this, but can't seem to find any good solutions. Help is much appreciated.
UPDATED: This is a rough draft of what I've worked out so far... it seems to do well even when filtering/comparing on the field in a query but isn't well tested just yet.
class TFDateTime(TypeDecorator):
impl = DATETIME
def process_bind_param(self, value, dialect):
if value is None:
return None
print("process_bind_param", value, type(value))
if type(value) == datetime or type(value) == date:
return value
elif type(value) == str:
return parse(value, ignoretz=True)
else:
return None
def process_result_value(self, value, dialect):
if value is None:
return None
print("process_result_value", value, type(value))
return value
回答1:
I'm surprised the built-in DateTime type doesn't automatically give a valid value to the DB - it might be a bug in SQLAlchemy?
That said, creating a custom type will probably do what you want - there's a few examples in the SQLAlchemy docs, but something like this should get you started:
import datetime
from sqlalchemy.types import TypeDecorator, TIMESTAMP
import iso8601
class ISO8601DateTime(TypeDecorator):
impl = TIMESTAMP
def process_bind_param(self, value, dialect):
if value is None:
return None
if isinstance(value, datetime.datetime):
return value.strftime(<iso8601-format-string>)
return value
def process_result_value(self, value, dialect):
if value is None:
return None
return iso8601.parse_date(value)
Uses the iso8601 lib for brevity, date-util might be necessary. You may also need to use dialect-specific types for timestamp/datetime.
来源:https://stackoverflow.com/questions/47717840/format-db-datetime-values-automatically-in-flask-sqlalchemy