Question

I have code like this working fine:

def get_timestamp(ts):
    return datetime.utcfromtimestamp(ts)

def set_timestamp(dt):
    return time.mktime(dt.timetuple())

class Group(Base):
    __tablename__ = 'group'
    _created = Column('created', Integer, nullable=False)

    @property
    def created(self):
        return get_timestamp(self._created)
    @created.setter
    def created(self, value):
        self._created = set_timestamp(value)

I want some code like this, but it's not working:

created = synonym('_created',
                  descriptor=property(get_timestamp,
                                      set_created))

Because it always passed in a self as the 1st param.

I'd like to use get_timestamp and set_timestamp across my project of cause. So I'm not going to make them methods of the class but stand alone function.

How can I achieve this?

EDIT: I would take Option2, and still open to other answers.

Was it helpful?

Solution

Option-1: Code below should work (you do not need to have a class in order to define self):

def pget_timestamp(self):
    return datetime.utcfromtimestamp(self._created)

def pset_timestamp(self, dt):
    self._created = time.mktime(dt.timetuple())

class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)
    _created = Column('created', Integer, nullable=False)

    created = synonym(_created,
            descriptor=property(pget_timestamp, pset_timestamp),
            )

Option-2: If you do need the same on many classes, leverage Mixins

from sqlalchemy.ext.declarative import declared_attr
class _CreatedMixin(object):
    _created = Column('created', Integer, nullable=False)

    def pget_timestamp(self):
        return datetime.utcfromtimestamp(self._created)

    def pset_timestamp(self, dt):
        self._created = time.mktime(dt.timetuple())

    @declared_attr
    def created(cls):
        return synonym('_created',
            descriptor=property(cls.pget_timestamp, cls.pset_timestamp),
            )


class Group(_CreatedMixin, Base):
    # @note: adding *_CreatedMixin* to bases defines both the column and the synonym
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)

Alternatively, if this is for all your classes, you could make _CreatedMixin a base class for all your models:

Base = declarative_base(engine, cls=_CreatedMixin)
class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)

Option-3: You could do any of the above using Hybrid Attributes


Note: make your set/get functions in-sync: either both or none use UTC-enabled functionality. Currently (unless you are in UTC-0) setting one value to created will not return the same one back.

OTHER TIPS

I'm now using a different implementation. It's not related to the original title, but in case you need it.

Use sqlalchemy.types.TypeDecorator. Defining a table with sqlalchemy with a mysql unix timestamp

class UTCTimestampType(TypeDecorator):
    impl = Integer

    def process_bind_param(self, value, dialect):
        if value is None:
            return None  # support nullability
        elif isinstance(value, datetime):
            return int(time.mktime(value.timetuple()))
        raise ValueError("Can operate only on datetime values. Offending value type: {0}".format(type(value).__name__))

    def process_result_value(self, value, dialect):
        if value is not None:  # support nullability
            return datetime.fromtimestamp(float(value))

class ModelA(Base):
    __tablename__ = 'model_a'
    id = Column(Integer, primary_key=True)
    created = Column(UTCTimestampType, nullable=False)

issues about alembic. Alembic: How to migrate custom type in a model?

# manually change the line
sa.Column('created', sa.UTCTImestampType(), nullable=False),
# to
sa.Column('created', sa.Integer(), nullable=False),
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top