1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- # -*- coding: utf-8 -*-
- from sqlalchemy.orm import Session
- from sqlalchemy import inspect
- from sqlalchemy.orm.unitofwork import UOWTransaction
- from transaction import TransactionManager
- from contextlib import contextmanager
-
- from tracim_backend.exceptions import ContentRevisionDeleteError
- from tracim_backend.exceptions import ContentRevisionUpdateError
- from tracim_backend.exceptions import SameValueError
-
- from tracim_backend.models.data import ContentRevisionRO
- from tracim_backend.models.data import Content
- from tracim_backend.models.meta import DeclarativeBase
-
-
- def prevent_content_revision_delete(
- session: Session,
- flush_context: UOWTransaction,
- instances: [DeclarativeBase]
- ) -> None:
- for instance in session.deleted:
- if isinstance(instance, ContentRevisionRO) \
- and instance.revision_id is not None:
- raise ContentRevisionDeleteError(
- "ContentRevision is not deletable. " +
- "You must make a new revision with" +
- "is_deleted set to True. Look at " +
- "tracim.model.new_revision context " +
- "manager to make a new revision"
- )
-
-
- class RevisionsIntegrity(object):
- """
- Simple static used class to manage a list with list of ContentRevisionRO
- who are allowed to be updated.
-
- When modify an already existing (understood have an identity in databse)
- ContentRevisionRO, if it's not in RevisionsIntegrity._updatable_revisions
- list, a ContentRevisionUpdateError thrown.
-
- This class is used by tracim.model.new_revision context manager.
- """
- _updatable_revisions = []
-
- @classmethod
- def add_to_updatable(cls, revision: 'ContentRevisionRO') -> None:
- if inspect(revision).has_identity:
- raise ContentRevisionUpdateError("ContentRevision is not updatable. %s already have identity." % revision) # nopep8
-
- if revision not in cls._updatable_revisions:
- cls._updatable_revisions.append(revision)
-
- @classmethod
- def remove_from_updatable(cls, revision: 'ContentRevisionRO') -> None:
- if revision in cls._updatable_revisions:
- cls._updatable_revisions.remove(revision)
-
- @classmethod
- def is_updatable(cls, revision: 'ContentRevisionRO') -> bool:
- return revision in cls._updatable_revisions
-
-
- @contextmanager
- def new_revision(
- session: Session,
- tm: TransactionManager,
- content: Content,
- force_create_new_revision: bool=False,
- ) -> Content:
- """
- Prepare context to update a Content. It will add a new updatable revision
- to the content.
- :param session: Database _session
- :param tm: TransactionManager
- :param content: Content instance to update
- :param force_create_new_revision: Decide if new_rev should or should not
- be forced.
- :return:
- """
- with session.no_autoflush:
- try:
- if force_create_new_revision \
- or inspect(content.revision).has_identity:
- content.new_revision()
- RevisionsIntegrity.add_to_updatable(content.revision)
- yield content
- except SameValueError or ValueError as e:
- # INFO - 20-03-2018 - renew transaction when error happened
- # This avoid bad _session data like new "temporary" revision
- # to be add when problem happen.
- tm.abort()
- tm.begin()
- raise e
- finally:
- RevisionsIntegrity.remove_from_updatable(content.revision)
|