Sqlalchemy mixins /和事件监听器

blu*_*ank 10 python events model sqlalchemy

我正在尝试同时处理2件新事物,因此我们非常感谢您在简化和澄清方面的帮助.

from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy import Column, Float, event

class TimeStampMixin(object):

    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

    created = Column(Float)
    modified = Column(Float)
    def __init__(self, created = None,
                       modified = None):
        self.created = created
        self.modified = modified

def create_time(mapper, connection, target):
    target.created = time()

#def modified_time(mapper, connection, target):
#    target.modified = time()

event.listen(TimeStampMixin, 'before_insert', create_time)
#event.listen(TimeStampMixin, 'before_update', modified_time)
Run Code Online (Sandbox Code Playgroud)

所以我想创建一个我可以在任何类中应用的mixin:

class MyClass(TimeStampMixin, Base):
    etc, etc, etc
Run Code Online (Sandbox Code Playgroud)

此类继承在创建时创建时间戳并在更新时创建/修改时间戳的功能.

在导入时我收到此错误:

raise exc.UnmappedClassError(class_)
sqlalchemy.orm.exc.UnmappedClassError: Class 'db.database.TimeStampMixin' is not mapped
Run Code Online (Sandbox Code Playgroud)

aaaand我在这一点上很难过.

deB*_*ice 35

将侦听器附加到类方法中,它会将事件附加到子类.

class TimeStampMixin(object):
    @staticmethod
    def create_time(mapper, connection, target):
        target.created = time()

    @classmethod
    def __declare_last__(cls):
        # get called after mappings are completed
        # http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#declare-last
        event.listen(cls, 'before_insert', cls.create_time)
Run Code Online (Sandbox Code Playgroud)

  • @SergeBelov我猜是使用`__declare_last__` (3认同)
  • 这绝对是一个更好的答案 - 我不知道`__declare_last__`. (2认同)
  • 如果有几个mixin使用`__declare_last__`,它们会被覆盖吗? (2认同)

Jef*_*ner 13

这是我要做的事情来监听before_insert事件:添加一个classmethod到您的TimeStampMixin注册当前类并处理设置创建时间.

例如

class TimeStampMixin(object):

    # other class methods

    @staticmethod
    def create_time(mapper, connection, target):
        target.created = time()

    @classmethod
    def register(cls):
        sqlalchemy.event.listen(cls, 'before_insert', cls.create_time)
Run Code Online (Sandbox Code Playgroud)

这样,你可以:

  1. 轻松扩展和更改您收听的内容以及注册的内容.
  2. 覆盖某些类的create_time方法
  3. 明确哪些方法需要设置时间戳.

你可以简单地使用它:

class MyMappedClass(TimeStampMixin, Base):
    pass

MyMappedClass.register()
Run Code Online (Sandbox Code Playgroud)

简单,非常清晰,没有魔力,但仍然像你想要的那样封装.


Mik*_*tak 5

现代 SqlAlchemy 中最好的方法是将@listens_for装饰器与propagate=True.

from datetime import datetime
from sqlalchemy import Column, Float
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.event import listens_for

class TimestampMixin():
    @declared_attr
    def created(cls):
        return Column(DateTime(timezone=True))

@listens_for(TimeStampMixin, "init", propagate=True)
def timestamp_init(target, args, kwargs):
    kwargs["created"] = datetime.utcnow()
Run Code Online (Sandbox Code Playgroud)