Exemple #1
0
 def save(self):
     self.logger.debug(".save()")
     db_session = DbSession()
     # Prevent expiration after the session is closed or object is made transient or disconnected
     db_session.expire_on_commit = False
     try:
         # No need to 'add', committing this class
         db_session.add(self)
         db_session.commit()
         # Keep it detached
         make_transient(self)
         make_transient_to_detached(self)
     except InvalidRequestError as e:
         self.logger.error(
             ".save() - Could not commit to {} table in database".format(
                 self.__tablename__),
             exc_info=True)
         self.__cleanupDbSession(db_session, self.__class__.__name__)
     except Exception as e:
         db_session.rollback()
         self.logger.error(
             ".save() - Could not commit to {} table in database".format(
                 self.__tablename__),
             exc_info=True)
         raise DbException(
             "Could not commit to {} table in database".format(
                 self.__tablename__))
Exemple #2
0
 def get_config():
     db_session = DbSession()
     # Prevent expiration after the session is closed or object is made transient or disconnected
     db_session.expire_on_commit = False
     ccm = None
     try:
         # Should be only one, return last modified
         ccm = db_session.query(ChargerConfigModel) \
                         .order_by(desc(ChargerConfigModel.modified_at)) \
                         .first()
         # Detach (not transient) from database, allows saving in other Threads
         # https://docs.sqlalchemy.org/en/14/orm/session_api.html#sqlalchemy.orm.make_transient_to_detached
         make_transient(ccm)
         make_transient_to_detached(ccm)
     except InvalidRequestError as e:
         ChargerConfigModel.__cleanupDbSession(db_session,
                                               ChargerConfigModel.__class__)
     except Exception as e:
         # Nothing to roll back
         ChargerConfigModel.logger.error(
             "Could not query from {} table in database".format(
                 ChargerConfigModel.__tablename__),
             exc_info=True)
         raise DbException(
             "Could not query from {} table in database".format(
                 ChargerConfigModel.__tablename__))
     return ccm
Exemple #3
0
def manufacture_persistent_object(
        session, specimen, values=None, primary_key=None):
    """Make an ORM-mapped object persistent in a Session without SQL.

    The persistent object is returned.

    If a matching object is already present in the given session, the specimen
    is merged into it and the persistent object returned.  Otherwise, the
    specimen itself is made persistent and is returned.

    The object must contain a full primary key, or provide it via the values or
    primary_key parameters.  The object is peristed to the Session in a "clean"
    state with no pending changes.

    :param session: A Session object.

    :param specimen: a mapped object which is typically transient.

    :param values: a dictionary of values to be applied to the specimen,
     in addition to the state that's already on it.  The attributes will be
     set such that no history is created; the object remains clean.

    :param primary_key: optional tuple-based primary key.  This will also
     be applied to the instance if present.


    """
    state = inspect(specimen)
    mapper = state.mapper

    for k, v in values.items():
        orm.attributes.set_committed_value(specimen, k, v)

    pk_attrs = [
        mapper.get_property_by_column(col).key
        for col in mapper.primary_key
    ]

    if primary_key is not None:
        for key, value in zip(pk_attrs, primary_key):
            orm.attributes.set_committed_value(
                specimen,
                key,
                value
            )

    for key in pk_attrs:
        if state.attrs[key].loaded_value is orm.attributes.NO_VALUE:
            raise ValueError("full primary key must be present")

    orm.make_transient_to_detached(specimen)

    if state.key not in session.identity_map:
        session.add(specimen)
        return specimen
    else:
        return session.merge(specimen, load=False)
Exemple #4
0
def manufacture_persistent_object(
        session, specimen, values=None, primary_key=None):
    """Make an ORM-mapped object persistent in a Session without SQL.

    The persistent object is returned.

    If a matching object is already present in the given session, the specimen
    is merged into it and the persistent object returned.  Otherwise, the
    specimen itself is made persistent and is returned.

    The object must contain a full primary key, or provide it via the values or
    primary_key parameters.  The object is peristed to the Session in a "clean"
    state with no pending changes.

    :param session: A Session object.

    :param specimen: a mapped object which is typically transient.

    :param values: a dictionary of values to be applied to the specimen,
     in addition to the state that's already on it.  The attributes will be
     set such that no history is created; the object remains clean.

    :param primary_key: optional tuple-based primary key.  This will also
     be applied to the instance if present.


    """
    state = inspect(specimen)
    mapper = state.mapper

    for k, v in values.items():
        orm.attributes.set_committed_value(specimen, k, v)

    pk_attrs = [
        mapper.get_property_by_column(col).key
        for col in mapper.primary_key
    ]

    if primary_key is not None:
        for key, value in zip(pk_attrs, primary_key):
            orm.attributes.set_committed_value(
                specimen,
                key,
                value
            )

    for key in pk_attrs:
        if state.attrs[key].loaded_value is orm.attributes.NO_VALUE:
            raise ValueError("full primary key must be present")

    orm.make_transient_to_detached(specimen)

    if state.key not in session.identity_map:
        session.add(specimen)
        return specimen
    else:
        return session.merge(specimen, load=False)
    def test_make_transient_to_detached(self):
        users, User = self.tables.users, self.classes.User

        mapper(User, users)
        sess = Session()
        u1 = User(id=1, name='test')
        sess.add(u1)
        sess.commit()
        sess.close()

        u2 = User(id=1)
        make_transient_to_detached(u2)
        assert 'id' in u2.__dict__
        sess.add(u2)
        eq_(u2.name, "test")
Exemple #6
0
    def test_make_transient_to_detached(self):
        users, User = self.tables.users, self.classes.User

        mapper(User, users)
        sess = Session()
        u1 = User(id=1, name='test')
        sess.add(u1)
        sess.commit()
        sess.close()

        u2 = User(id=1)
        make_transient_to_detached(u2)
        assert 'id' in u2.__dict__
        sess.add(u2)
        eq_(u2.name, "test")
Exemple #7
0
    def test_already_attached(self):
        User = self.classes.User
        users = self.tables.users
        mapper(User, users)

        s1 = Session()
        s2 = Session()

        u1 = User(id=1, name='u1')
        make_transient_to_detached(u1)  # shorthand for actually persisting it
        s1.add(u1)

        assert_raises_message(
            sa.exc.InvalidRequestError,
            "Object '<User.*?>' is already attached to session", s2.add, u1)
        assert u1 not in s2
        assert not s2.identity_map.keys()
    def test_already_attached(self):
        User = self.classes.User
        users = self.tables.users
        mapper(User, users)

        s1 = Session()
        s2 = Session()

        u1 = User(id=1, name='u1')
        make_transient_to_detached(u1)  # shorthand for actually persisting it
        s1.add(u1)

        assert_raises_message(
            sa.exc.InvalidRequestError,
            "Object '<User.*?>' is already attached to session",
            s2.add, u1
        )
        assert u1 not in s2
        assert not s2.identity_map.keys()
Exemple #9
0
 def delete(self):
     db_session = DbSession()
     db_session.expire_on_commit = True
     try:
         db_session.delete(self)
         db_session.commit()
         # Keep it detached
         make_transient(self)
         make_transient_to_detached(self)
     except InvalidRequestError as e:
         self.__cleanupDbSession(db_session, self.__class__.__name__)
     except Exception as e:
         db_session.rollback()
         self.logger.error(
             "Could not delete from {} table in database".format(
                 self.__tablename__),
             exc_info=True)
         raise DbException(
             "Could not delete from {} table in database".format(
                 self.__tablename__))
    def new_version(self, session):

        # our current identity key, which will be used on the "old"
        # version of us to emit an UPDATE. this is just for assertion purposes
        old_identity_key = inspect(self).key

        # make sure self.start / self.end are not expired
        self.id, self.start, self.end

        # turn us into an INSERT
        make_transient(self)

        # make the "old" version of us, which we will turn into an
        # UPDATE
        old_copy_of_us = self.__class__(
            id=self.id, start=self.start, end=self.end
        )

        # turn old_copy_of_us into an UPDATE
        make_transient_to_detached(old_copy_of_us)

        # the "old" object has our old identity key (that we no longer have)
        assert inspect(old_copy_of_us).key == old_identity_key

        # now put it back in the session
        session.add(old_copy_of_us)

        # now update the 'end' - SQLAlchemy sees this as a PK switch
        old_copy_of_us.end = current_time()

        # fun fact!  the new_version() routine is *not* called for
        # old_copy_of_us!  because we are already in the before_flush() hook!
        # this surprised even me.   I was thinking we had to guard against
        # it.  Still might be a good idea to do so.

        self.start = current_time()
        self.end = current_time() + datetime.timedelta(days=2)
Exemple #11
0
    def new_version(self, session):

        # our current identity key, which will be used on the "old"
        # version of us to emit an UPDATE. this is just for assertion purposes
        old_identity_key = inspect(self).key

        # make sure self.start / self.end are not expired
        self.id, self.start, self.end

        # turn us into an INSERT
        make_transient(self)

        # make the "old" version of us, which we will turn into an
        # UPDATE
        old_copy_of_us = self.__class__(id=self.id,
                                        start=self.start,
                                        end=self.end)

        # turn old_copy_of_us into an UPDATE
        make_transient_to_detached(old_copy_of_us)

        # the "old" object has our old identity key (that we no longer have)
        assert inspect(old_copy_of_us).key == old_identity_key

        # now put it back in the session
        session.add(old_copy_of_us)

        # now update the 'end' - SQLAlchemy sees this as a PK switch
        old_copy_of_us.end = current_time()

        # fun fact!  the new_version() routine is *not* called for
        # old_copy_of_us!  because we are already in the before_flush() hook!
        # this surprised even me.   I was thinking we had to guard against
        # it.  Still might be a good idea to do so.

        self.start = current_time()
        self.end = current_time() + datetime.timedelta(days=2)