Ich versuche zwei neue Dinge gleichzeitig, daher wird die Unterstützung bei der Vereinfachung und Klärung geschätzt.

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)

Ich möchte also ein Mixin erstellen, das ich in jeder Klasse anwenden kann:

class MyClass(TimeStampMixin, Base):
    etc, etc, etc

Diese Klasse erbt Funktionen, die beim Erstellen einen Zeitstempel erstellen und beim Aktualisieren einen Zeitstempel erstellen / ändern.

Beim Import erhalte ich folgende Fehlermeldung:

raise exc.UnmappedClassError(class_)
sqlalchemy.orm.exc.UnmappedClassError: Class 'db.database.TimeStampMixin' is not mapped

Aaaund ich bin an diesem Punkt ratlos.

11
blueblank 6 Okt. 2012 im 00:17

4 Antworten

Beste Antwort

Folgendes würde ich tun, um before_insert Ereignisse abzuhören: Fügen Sie Ihrem TimeStampMixin ein classmethod hinzu, das die aktuelle Klasse registriert und die Einstellung der Erstellungszeit übernimmt.

Z.B.

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)

Auf diese Weise können Sie:

  1. Erweitern und ändern Sie ganz einfach, worauf Sie hören und was Sie registrieren.
  2. Überschreiben Sie die Methode create_time für bestimmte Klassen
  3. Geben Sie genau an, für welche Methoden die Zeitstempel festgelegt werden müssen.

Sie können es einfach verwenden:

class MyMappedClass(TimeStampMixin, Base):
    pass

MyMappedClass.register()

Einfach, sehr klar, keine Magie, aber immer noch so, wie Sie es möchten.

13
Drachenfels 12 Feb. 2015 im 14:24

Fügen Sie Ihren Listener innerhalb der Klassenmethode hinzu, und das Ereignis wird an die untergeordnete Klasse angehängt.

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)
35
deBrice 10 Dez. 2012 im 02:16

Der beste Weg in der modernen SqlAlchemy ist die Verwendung des @listens_for Dekorators mit 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()
2
Mike Haboustak 30 Mai 2017 im 12:44

Sie können es auch so machen:

from sqlalchemy.orm.interfaces import MapperExtension

class BaseExtension(MapperExtension):
    """Base entension class for all entities """

    def before_insert(self, mapper, connection, instance):
        """ set the created_at  """
        instance.created = datetime.datetime.now()

    def before_update(self, mapper, connection, instance):
        """ set the updated_at  """
        instance.modified = datetime.datetime.now()


class TimeStampMixin(object):
    id = Column(Integer, primary_key=True, autoincrement=True)
    created = Column(DateTime())
    modified = Column(DateTime())

    __table_args__ = {
        'mysql_engine': 'InnoDB',
        'mysql_charset': 'utf8'
    }
    __mapper_args__ = { 'extension': BaseExtension() }

Und definieren Sie Ihre Klassen wie:

class User(TimeStampMixin, Base):
2
Raul Gomez 6 Okt. 2012 im 17:10