forked from Raiza.dev/EliteBot
96 lines
2.8 KiB
Python
96 lines
2.8 KiB
Python
from datetime import datetime
|
|
|
|
import sqlalchemy as sa
|
|
|
|
|
|
class Timestamp:
|
|
"""Adds `created` and `updated` columns to a derived declarative model.
|
|
|
|
The `created` column is handled through a default and the `updated`
|
|
column is handled through a `before_update` event that propagates
|
|
for all derived declarative models.
|
|
|
|
::
|
|
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy_utils import Timestamp
|
|
|
|
|
|
class SomeModel(Base, Timestamp):
|
|
__tablename__ = 'somemodel'
|
|
id = sa.Column(sa.Integer, primary_key=True)
|
|
"""
|
|
|
|
created = sa.Column(sa.DateTime, default=datetime.utcnow, nullable=False)
|
|
updated = sa.Column(sa.DateTime, default=datetime.utcnow, nullable=False)
|
|
|
|
|
|
@sa.event.listens_for(Timestamp, 'before_update', propagate=True)
|
|
def timestamp_before_update(mapper, connection, target):
|
|
# When a model with a timestamp is updated; force update the updated
|
|
# timestamp.
|
|
target.updated = datetime.utcnow()
|
|
|
|
|
|
NOT_LOADED_REPR = '<not loaded>'
|
|
|
|
|
|
def _generic_repr_method(self, fields):
|
|
state = sa.inspect(self)
|
|
field_reprs = []
|
|
if not fields:
|
|
fields = state.mapper.columns.keys()
|
|
for key in fields:
|
|
value = state.attrs[key].loaded_value
|
|
if key in state.unloaded:
|
|
value = NOT_LOADED_REPR
|
|
else:
|
|
value = repr(value)
|
|
field_reprs.append('='.join((key, value)))
|
|
|
|
return '{}({})'.format(self.__class__.__name__, ', '.join(field_reprs))
|
|
|
|
|
|
def generic_repr(*fields):
|
|
"""Adds generic ``__repr__()`` method to a declarative SQLAlchemy model.
|
|
|
|
In case if some fields are not loaded from a database, it doesn't
|
|
force their loading and instead repesents them as ``<not loaded>``.
|
|
|
|
In addition, user can provide field names as arguments to the decorator
|
|
to specify what fields should present in the string representation
|
|
and in what order.
|
|
|
|
Example::
|
|
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy_utils import generic_repr
|
|
|
|
|
|
@generic_repr
|
|
class MyModel(Base):
|
|
__tablename__ = 'mymodel'
|
|
id = sa.Column(sa.Integer, primary_key=True)
|
|
name = sa.Column(sa.String)
|
|
category = sa.Column(sa.String)
|
|
|
|
session.add(MyModel(name='Foo', category='Bar'))
|
|
session.commit()
|
|
foo = session.query(MyModel).options(sa.orm.defer('category')).one(s)
|
|
|
|
assert repr(foo) == 'MyModel(id=1, name='Foo', category=<not loaded>)'
|
|
"""
|
|
if len(fields) == 1 and callable(fields[0]):
|
|
target = fields[0]
|
|
target.__repr__ = lambda self: _generic_repr_method(self, fields=None)
|
|
return target
|
|
else:
|
|
def decorator(cls):
|
|
cls.__repr__ = lambda self: _generic_repr_method(
|
|
self,
|
|
fields=fields
|
|
)
|
|
return cls
|
|
return decorator
|