Format db.Datetime values automatically in Flask-SQLAlchemy

落花浮王杯 提交于 2019-12-24 19:26:37

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!