fix: show display text for related objects, in version diff
This commit is contained in:
parent
5b6c686a9d
commit
2723965a6a
2 changed files with 98 additions and 25 deletions
|
|
@ -150,9 +150,9 @@ class Diff:
|
||||||
|
|
||||||
class VersionDiff(Diff):
|
class VersionDiff(Diff):
|
||||||
"""
|
"""
|
||||||
Special diff class, for use with version history views. Note that
|
Special diff class for use with version history views. While
|
||||||
while based on :class:`Diff`, this class uses a different
|
based on :class:`Diff`, this class uses a different signature for
|
||||||
signature for the constructor.
|
the constructor.
|
||||||
|
|
||||||
:param version: Reference to a Continuum version record (object).
|
:param version: Reference to a Continuum version record (object).
|
||||||
|
|
||||||
|
|
@ -208,17 +208,76 @@ class VersionDiff(Diff):
|
||||||
|
|
||||||
return [field for field in fields if field not in unwanted]
|
return [field for field in fields if field not in unwanted]
|
||||||
|
|
||||||
def render_version_value(self, value): # pylint: disable=missing-function-docstring
|
def render_version_value(self, version, field, value):
|
||||||
return HTML.tag("span", c=[repr(value)], style="font-family: monospace;")
|
"""
|
||||||
|
Render the cell value HTML for a given version + field.
|
||||||
|
|
||||||
|
This method is used to render both sides of the diff (old +
|
||||||
|
new values). It will just render the field value using a
|
||||||
|
monospace font by default. However:
|
||||||
|
|
||||||
|
If the field is involved in a mapper relationship (i.e. it is
|
||||||
|
the "foreign key" to a related table), the logic here will
|
||||||
|
also (try to) traverse that show display text for the related
|
||||||
|
object (if found).
|
||||||
|
|
||||||
|
:param version: Reference to the Continuum version object.
|
||||||
|
|
||||||
|
:param field: Name of the field, as string.
|
||||||
|
|
||||||
|
:param value: Raw value for the field, as obtained from the
|
||||||
|
version object.
|
||||||
|
|
||||||
|
:returns: Rendered cell value as HTML literal
|
||||||
|
"""
|
||||||
|
# first render normal span; this is our fallback but also may
|
||||||
|
# be embedded within a more complex result.
|
||||||
|
text = HTML.tag("span", c=[repr(value)], style="font-family: monospace;")
|
||||||
|
|
||||||
|
# loop thru all mapped relationship props
|
||||||
|
for prop in self.mapper.relationships:
|
||||||
|
|
||||||
|
# we only want singletons
|
||||||
|
if prop.uselist:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# loop thru columns for prop
|
||||||
|
# nb. there should always be just one colum for a
|
||||||
|
# singleton prop, but technically a list is used, so no
|
||||||
|
# harm in looping i assume..
|
||||||
|
for col in prop.local_columns:
|
||||||
|
|
||||||
|
# we only want the matching column
|
||||||
|
if col.name != field:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# grab "related version" reference via prop key. this
|
||||||
|
# would be like a UserVersion for instance.
|
||||||
|
if ref := getattr(version, prop.key):
|
||||||
|
|
||||||
|
# grab "related object" reference. this would be
|
||||||
|
# like a User for instance.
|
||||||
|
if ref := getattr(ref, "version_parent", None):
|
||||||
|
|
||||||
|
# render text w/ related object as bold string
|
||||||
|
style = (
|
||||||
|
"margin-left: 2rem; font-style: italic; font-weight: bold;"
|
||||||
|
)
|
||||||
|
return HTML.tag(
|
||||||
|
"span",
|
||||||
|
c=[text, HTML.tag("span", c=[str(ref)], style=style)],
|
||||||
|
)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
def render_old_value(self, field):
|
def render_old_value(self, field):
|
||||||
if self.nature == "create":
|
if self.nature == "create":
|
||||||
return ""
|
return ""
|
||||||
value = self.old_value(field)
|
value = self.old_value(field)
|
||||||
return self.render_version_value(value)
|
return self.render_version_value(self.version.previous, field, value)
|
||||||
|
|
||||||
def render_new_value(self, field):
|
def render_new_value(self, field):
|
||||||
if self.nature == "delete":
|
if self.nature == "delete":
|
||||||
return ""
|
return ""
|
||||||
value = self.new_value(field)
|
value = self.new_value(field)
|
||||||
return self.render_version_value(value)
|
return self.render_version_value(self.version, field, value)
|
||||||
|
|
|
||||||
|
|
@ -174,11 +174,15 @@ class TestVersionDiff(VersionWebTestCase):
|
||||||
["active", "person_uuid", "prevent_edit", "username", "uuid"],
|
["active", "person_uuid", "prevent_edit", "username", "uuid"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_render_values(self):
|
def test_render_version_value(self):
|
||||||
import sqlalchemy_continuum as continuum
|
import sqlalchemy_continuum as continuum
|
||||||
|
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
user = model.User(username="fred")
|
person = model.Person(full_name="Fred Flintstone")
|
||||||
|
self.session.add(person)
|
||||||
|
|
||||||
|
# create, update, delete user
|
||||||
|
user = model.User(username="fred", person=person)
|
||||||
self.session.add(user)
|
self.session.add(user)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
user.username = "freddie"
|
user.username = "freddie"
|
||||||
|
|
@ -191,32 +195,42 @@ class TestVersionDiff(VersionWebTestCase):
|
||||||
versions = self.session.query(vercls).order_by(vercls.transaction_id).all()
|
versions = self.session.query(vercls).order_by(vercls.transaction_id).all()
|
||||||
self.assertEqual(len(versions), 3)
|
self.assertEqual(len(versions), 3)
|
||||||
|
|
||||||
|
# create (1st version)
|
||||||
version = versions[0]
|
version = versions[0]
|
||||||
diff = self.make_diff(version)
|
diff = self.make_diff(version)
|
||||||
self.assertEqual(diff.nature, "create")
|
self.assertEqual(diff.nature, "create")
|
||||||
self.assertEqual(diff.render_old_value("username"), "")
|
self.assertEqual(diff.render_old_value("username"), "")
|
||||||
self.assertEqual(
|
self.assertIn("fred", diff.render_new_value("username"))
|
||||||
diff.render_new_value("username"),
|
self.assertNotIn("freddie", diff.render_new_value("username"))
|
||||||
'<span style="font-family: monospace;">'fred'</span>',
|
self.assertEqual(diff.render_old_value("person_uuid"), "")
|
||||||
)
|
# rendered person_uuid includes display name
|
||||||
|
html = diff.render_new_value("person_uuid")
|
||||||
|
self.assertIn(str(person.uuid), html)
|
||||||
|
self.assertIn("Fred Flintstone", html)
|
||||||
|
|
||||||
|
# update (2nd version)
|
||||||
version = versions[1]
|
version = versions[1]
|
||||||
diff = self.make_diff(version)
|
diff = self.make_diff(version)
|
||||||
self.assertEqual(diff.nature, "update")
|
self.assertEqual(diff.nature, "update")
|
||||||
self.assertEqual(
|
self.assertIn("fred", diff.render_old_value("username"))
|
||||||
diff.render_old_value("username"),
|
self.assertNotIn("freddie", diff.render_old_value("username"))
|
||||||
'<span style="font-family: monospace;">'fred'</span>',
|
self.assertIn("freddie", diff.render_new_value("username"))
|
||||||
)
|
# rendered person_uuid includes display name
|
||||||
self.assertEqual(
|
html = diff.render_old_value("person_uuid")
|
||||||
diff.render_new_value("username"),
|
self.assertIn(str(person.uuid), html)
|
||||||
'<span style="font-family: monospace;">'freddie'</span>',
|
self.assertIn("Fred Flintstone", html)
|
||||||
)
|
html = diff.render_new_value("person_uuid")
|
||||||
|
self.assertIn(str(person.uuid), html)
|
||||||
|
self.assertIn("Fred Flintstone", html)
|
||||||
|
|
||||||
|
# delete (3rd version)
|
||||||
version = versions[2]
|
version = versions[2]
|
||||||
diff = self.make_diff(version)
|
diff = self.make_diff(version)
|
||||||
self.assertEqual(diff.nature, "delete")
|
self.assertEqual(diff.nature, "delete")
|
||||||
self.assertEqual(
|
self.assertIn("freddie", diff.render_old_value("username"))
|
||||||
diff.render_old_value("username"),
|
|
||||||
'<span style="font-family: monospace;">'freddie'</span>',
|
|
||||||
)
|
|
||||||
self.assertEqual(diff.render_new_value("username"), "")
|
self.assertEqual(diff.render_new_value("username"), "")
|
||||||
|
# rendered person_uuid includes display name
|
||||||
|
html = diff.render_old_value("person_uuid")
|
||||||
|
self.assertIn(str(person.uuid), html)
|
||||||
|
self.assertIn("Fred Flintstone", html)
|
||||||
|
self.assertEqual(diff.render_new_value("person_uuid"), "")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue