Update documents/revisions relationship/tables.

This commit is contained in:
Felipe Monteiro 2017-07-23 18:04:14 +01:00
parent 8b79789425
commit dbc80fbfae
3 changed files with 68 additions and 66 deletions

View File

@ -122,7 +122,8 @@ def document_create(values, session=None):
session = session or get_session()
filters = models.Document.UNIQUE_CONSTRAINTS
existing_document = document_get(**{c: values[c] for c in filters})
existing_document = document_get(**{c: values[c] for c in filters
if c != 'revision_id'})
created_document = {}
@ -144,11 +145,14 @@ def document_create(values, session=None):
# Only generate a new revision and entirely new document if anything
# was changed.
if _document_changed():
revision = revision_create_parent(existing_document)
values['revision_id'] = revision['id']
created_document = _document_create()
revision_update(created_document['id'], existing_document['id'])
revision_update_child(existing_document, created_document)
else:
revision = revision_create()
values['revision_id'] = revision['id']
created_document = _document_create()
revision_create(created_document['id'])
return created_document
@ -162,63 +166,69 @@ def document_get(session=None, **filters):
####################
def revision_create(document_id, session=None):
def revision_create(session=None):
session = session or get_session()
revision = models.Revision()
with session.begin():
revision.update({'document_id': document_id})
revision.save(session=session)
return revision.to_dict()
def revision_get(document_id, session=None):
def revision_get(revision_id, session=None):
session = session or get_session()
revision = session.query(models.Revision)\
.filter_by(document_id=document_id).first()
revision = session.query(models.Revision).get(revision_id)
return revision.to_dict()
def revision_update(document_id, child_document_id, session=None):
"""Create a parent revision and update the child revision.
def revision_create_parent(child_document, session=None):
"""Create a parent revision.
The ``document_id`` references the newly created document that is a more
up-to-date revision. Create a new (parent) revision that references
``document_id`` and whose ``child_id`` is ``child_document_id``.
Set the ``parent_id`` for ``child_revision`` to ``document_id``.
Create a new (parent) revision that references whose ``child_id`` is
the ID of ``child_document``.
After this function has executed, the following relationship is true:
parent_document <-- parent_revision
^ /
\ (has child)
\ /
\ /
\ /
/ \
/ \
/ \
/ (has parent)
v \
child_document <-- child_revision
parent_document --> parent_revision
|
(has child)
v
child_document --> child_revision
:param document_id: The ID corresponding to the up-to-date document.
:param child_document_id: The ID corresponding tothe out-of-date document.
:param child_document: The out-of-date document.
:param session: The database session.
:returns: The dictionary representation of the newly created revision.
"""
session = session or get_session()
parent_revision = models.Revision()
with session.begin():
parent_revision.update({'document_id': document_id,
'child_id': child_document_id})
parent_revision.update({'child_id': child_document['revision_id']})
parent_revision.save(session=session)
child_revision = session.query(models.Revision)\
.filter_by(document_id=child_document_id).first()
return parent_revision.to_dict()
def revision_update_child(child_document, parent_document, session=None):
"""Update the child revision for an out-of-date document.
After this function has executed, the following relationship is true:
parent_document --> parent_revision
| ^
(has child) (has parent)
v |
child_document --> child_revision
:param child_document: The out-of-date document.
:param parent_document: The up-to-date document.
:param session: The database session.
:returns: The dictionary representation of the ``child_revision``.
"""
session = session or get_session()
child_revision = session.query(models.Revision).get(
child_document['revision_id'])
with session.begin():
child_revision.update({'parent_id': document_id})
child_revision.update({'parent_id': parent_document['revision_id']})
child_revision.save(session=session)
return parent_revision.to_dict()
return child_revision.to_dict()

View File

@ -128,17 +128,14 @@ class Revision(BASE, DeckhandBase):
id = Column(String(36), primary_key=True,
default=lambda: str(uuid.uuid4()))
document_id = Column(Integer, ForeignKey('documents.id'), nullable=True)
parent_id = Column(Integer, ForeignKey('documents.id'), nullable=True)
child_id = Column(Integer, ForeignKey('documents.id'), nullable=True)
document = relationship("Document", back_populates="revision",
foreign_keys=[document_id])
parent_id = Column(Integer, ForeignKey('revisions.id'), nullable=True)
child_id = Column(Integer, ForeignKey('revisions.id'), nullable=True)
class Document(BASE, DeckhandBase):
UNIQUE_CONSTRAINTS = ('schema_version', 'kind')
UNIQUE_CONSTRAINTS = ('schema_version', 'kind', 'revision_id')
__tablename__ = 'documents'
#__table_args__ = (DeckhandBase.gen_unqiue_contraint(*UNIQUE_CONSTRAINTS),)
__table_args__ = (DeckhandBase.gen_unqiue_contraint(*UNIQUE_CONSTRAINTS),)
id = Column(String(36), primary_key=True,
default=lambda: str(uuid.uuid4()))
@ -149,9 +146,11 @@ class Document(BASE, DeckhandBase):
# "metadata" is reserved, so use "doc_metadata" instead.
doc_metadata = Column(JSONEncodedDict(), nullable=False)
data = Column(JSONEncodedDict(), nullable=False)
revision = relationship("Revision", uselist=False,
back_populates="document",
foreign_keys="[Revision.document_id]")
revision_id = Column(Integer, ForeignKey('revisions.id'), nullable=False)
revision = relationship("Revision",
foreign_keys=[revision_id],
cascade="all, delete")
def register_models(engine):

View File

@ -46,7 +46,7 @@ class TestDocumentsApi(base.DeckhandWithDBTestCase):
self.assertEqual(val, actual[key])
def _validate_revision(self, revision):
expected_attrs = ('id', 'document_id', 'child_id', 'parent_id')
expected_attrs = ('id', 'child_id', 'parent_id')
for attr in expected_attrs:
self.assertIn(attr, revision)
self.assertThat(revision[attr], matchers.MatchesAny(
@ -57,26 +57,19 @@ class TestDocumentsApi(base.DeckhandWithDBTestCase):
document = db_api.document_create(fixture)
self._validate_document(fixture, document)
revision = db_api.revision_get(document['id'])
revision = db_api.revision_get(document['revision_id'])
self._validate_revision(revision)
self.assertEqual(document['id'], revision['document_id'])
self.assertEqual(document['revision_id'], revision['id'])
def test_create_and_update_document(self):
"""
Check that the following relationship is true:
parent_document <-- parent_revision
^ /
\ (has child)
\ /
\ /
\ /
/ \
/ \
/ \
/ (has parent)
v \
child_document <-- child_revision
parent_document --> parent_revision
| ^
(has child) (has parent)
v |
child_document --> child_revision
"""
fixture = DocumentFixture().get_minimal_fixture()
child_document = db_api.document_create(fixture)
@ -92,13 +85,13 @@ class TestDocumentsApi(base.DeckhandWithDBTestCase):
# Validate that the parent document has a different revision and
# that the revisions and document links are correct.
child_revision = db_api.revision_get(child_document['id'])
parent_revision = db_api.revision_get(parent_document['id'])
child_revision = db_api.revision_get(child_document['revision_id'])
parent_revision = db_api.revision_get(parent_document['revision_id'])
for revision in (child_revision, parent_revision):
self._validate_revision(revision)
self.assertNotEqual(child_revision['id'], parent_revision['id'])
self.assertEqual(parent_document['id'],
parent_revision['document_id'])
self.assertEqual(child_document['id'], parent_revision['child_id'])
self.assertEqual(parent_document['id'], child_revision['parent_id'])
self.assertEqual(parent_document['revision_id'],
parent_revision['id'])
self.assertEqual(child_document['revision_id'], child_revision['id'])
self.assertEqual(parent_document['revision_id'], parent_revision['id'])