diff --git a/CHANGELOG.md b/CHANGELOG.md index 6255ff1c0286f518b9dbd333bd3f61dd690e5cb4..5aa592990ce2b80b9fde674959246c38c4278bb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added ### +* is_reference function for Properties + ### Changed ### ### Deprecated ### diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index 0142b04477c600bade9603c1de59e850afed931e..57b07bd6f83cf2dd4d0523a70308ee189a96a995 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -31,6 +31,7 @@ from __future__ import print_function, unicode_literals import re import sys from builtins import str +from copy import deepcopy from functools import cmp_to_key from hashlib import sha512 from os import listdir @@ -1504,6 +1505,42 @@ class Property(Entity): return super(Property, self).to_xml(xml, add_properties) + def is_reference(self, server_retrieval=False): + """Returns whether this Property is a reference + + Parameters + ---------- + server_retrieval : bool, optional + If True and the datatype is not set, the Property is retrieved from the server, by default False + + Returns + ------- + bool, NoneType + Returns whether this Property is a reference or None if a server call is needed to + check correctly, but server_retrieval is set to False. + + """ + + if self.datatype is None: + + if not self.is_valid(): + # this is a workaround to prevent side effects + # since retrieve currently changes the object + + if server_retrieval: + tmp_prop = deepcopy(self) + tmp_prop.retrieve() + + return tmp_prop.is_reference() + else: + return None + else: + # a valid property without datatype has to be an RT + + return True + else: + return is_reference(self.datatype) + class Message(object): diff --git a/unittests/test_property.py b/unittests/test_property.py index 752ee01f0eafef14dbffd1e62c99d1c816c45d05..834b1be582c58c60f70331de9cb0d0d6414fd6c9 100644 --- a/unittests/test_property.py +++ b/unittests/test_property.py @@ -24,9 +24,10 @@ # ** end header # """Tests for the Property class.""" +import caosdb as db +from caosdb import Entity, Property, Record # pylint: disable=missing-docstring from lxml import etree -from caosdb import Entity, Property, Record parser = etree.XMLParser(remove_comments=True) testrecord = Record._from_xml(Record(), @@ -89,3 +90,47 @@ def test_get_property_with_entity(): def test_selected_reference_list(): assert len(testrecord.get_property("Conductor").value) == 1 assert isinstance(testrecord.get_property("Conductor").value[0], Entity) + + +def test_is_reference(): + PROPS = { + 10: db.INTEGER, + 20: db.REFERENCE, + 30: "SomeRT", + } + + def dummy_retrieve(self): + self.datatype = PROPS[self.id] + self.is_valid = lambda: True + # replace retrieve function by dummy + real_retrieve = Entity.retrieve + Entity.retrieve = dummy_retrieve + + p1 = Property(id=1, datatype=db.INTEGER) + p2 = Property(id=2, datatype=db.DOUBLE) + p3 = Property(id=3, datatype=db.TEXT) + p4 = Property(id=4, datatype=db.DATETIME) + p5 = Property(id=5, datatype=db.BOOLEAN) + p6 = Property(id=6, datatype=db.REFERENCE) + assert p1.is_reference() is False + assert p2.is_reference() is False + assert p3.is_reference() is False + assert p4.is_reference() is False + assert p5.is_reference() is False + assert p6.is_reference() is True + + p7 = Property(id=7) + p8 = Property(id=8, value=db.RecordType(id=1000)) + p8.is_valid = lambda: True + assert p7.is_reference() is None # cannot be resolved without calling a server + assert p8.is_reference() is True + + p10 = Property(id=10) + p20 = Property(id=20) + p30 = Property(id=30) + assert p10.is_reference(server_retrieval=True) is False + assert p20.is_reference(server_retrieval=True) is True + assert p30.is_reference(server_retrieval=True) is True + + # restore retrieve function with original + Entity.retrieve = real_retrieve