Skip to main content

Share information between entities

New in v12.12.0

entity_name has been added in the signature of controller methods and callback functions

New in v12.10.0

The following methods were added to the API class:

  • create_child_entity (not of file-type)
  • delete_entity
  • get_entities_by_type
  • get_entity_children
  • get_entity_parent
  • get_entity_revisions
  • get_entity_siblings
  • rename_entity
  • set_entity_params

The following methods were added to the Entity class:

  • create_child (not of file-type)
  • delete
  • revisions
  • rename
  • set_params

Sometimes information is needed from another entity. For example, when you want to show all the child locations on a MapView. Or when you require general settings, which are specified on the parent entity. This guide explains how to achieve this.

Using the API


Please keep in mind that API requests are relatively slow, so make sure you only execute them when necessary.

The API class (stands for "application programming interface") can be instantiated using the api_v1 module:

from viktor.api_v1 import API

api = API()

There are three starting points to obtain an entity/entities: work from the root entities (api.get_root_entities()), get all entities of a given type (api.get_entities_by_type('MyType')) or start with a given entity id:

api.get_entity(entity_id)  # returns the current entity, type Entity

Next to 'getting' the entity by its id, it is also possible to navigate to relatives (get_entity_children, get_entity_siblings, get_entity_parent), modify the corresponding entity (rename_entity, delete_entity, set_entity_params), get its history (get_entity_revisions), get the corresponding file (get_entity_file), or create a child entity (create_child_entity), directly by using the API methods.

For convenience, the Entity object itself also provides (most of) above methods. So it is also possible to navigate by:


It's possible to chain these methods, e.g.:

grand_parent = entity.parent().parent()

And filter on entity type:

gef_children = entity.children(entity_type_names=['GEF'])

In case only the id, name or type of an Entity is required, the performance of the API call(s) can be enhanced by setting the include_params flag to False:

gef_children = entity.children(entity_type_names=['GEF'], include_params=False)

The params of an Entity can simply be obtained by calling last_saved_params. In a similar way, the summary can be obtained by calling last_saved_summary:

params = entity.last_saved_params
summary = entity.last_saved_summary

Current entity and relative entities

The entity_id and entity_name of the current entity are sent along within all view-methods and callback-functions. The id can be used to construct a current_entity object, from which you can navigate to relatives.

For example within a view-method:

from viktor.api_v1 import API@DataView("Data", duration_guess=1)def calculate_view(self, params, entity_id, entity_name, **kwargs):    current_entity = API().get_entity(entity_id)    parent = current_entity.parent()    parent_params = parent.last_saved_params    # do something with parent_params    return DataResult(...)

Or a callback-function in the parametrization:

from viktor.api_v1 import APIfrom viktor.parametrization import ViktorParametrization, OptionFielddef get_options(params, entity_id, entity_name, **kwargs):    current_entity = API().get_entity(entity_id)    parent = current_entity.parent()    parent_params = parent.last_saved_params    # do something with parent_params    return [...]class MyParametrization(ViktorParametrization):    field = OptionField(..., options=get_options)

Within a single job (see VIKTOR's call flow for the possible triggers of a job) the results of API calls are temporarily stored (memoized). This means that multiple calls to the same entity only require a single request in the background. This is particularly handy for option functions:

from viktor.api_v1 import APIdef get_options1_from_parent(params, entity_id, **kwargs):    parent = API().get_entity(entity_id).parent()    parent_params = parent.last_saved_params    # get options1 from parent_params    ...def get_options2_from_parent(params, entity_id, **kwargs):    parent = API().get_entity(entity_id).parent()    parent_params = parent.last_saved_params    # get options2 from parent_params    ...class Parametrization(ViktorParametrization):    option1 = OptionField('Option 1', options=get_options1_from_parent)    option2 = OptionField('Option 2', options=get_options2_from_parent)


To enable a higher performance, an EntityList is returned instead of a list with Entity instances. In practice this makes little difference, allowing most normal list operations:

children = entity.children()  # children is of type `EntityList`len(children)  # okchildren[0]  # positive indexing: okchildren[-1]  # negative indexing: ok (>= v12.1.0)for child in children:  # iterating: ok    # do something with child

Slicing the EntityList is not supported (e.g. children[0:5]).

Entity selection fields

New in v13.0.0

EntityOptionField and EntityMultiSelectField can be used for selecting entities of given type. This will greatly improve the speed of apps where option fields are dynamically filled with entities.

Changed in v12.8.0

Entity selection fields return an Entity object instead of an entity id (integer) when the controller flag viktor_convert_entity_field is set to True. This affects the following fields:

  • ChildEntityOptionField
  • SiblingEntityOptionField
  • ChildEntityMultiSelectField
  • SiblingEntityMultiSelectField

When using entity selection fields (e.g. EntityOptionField or similar), the entity selected by the user is returned in the params as Entity object.

from viktor import ViktorControllerfrom viktor.parametrization import ViktorParametrization, EntityOptionFieldclass MyParametrization(ViktorParametrization):        cpt_entity = EntityOptionField('Select a CPT', entity_type_names=['CPTFile'])class MyController(ViktorController):    ...    @DataView("CPT data", duration_guess=3)    def visualize(self, params, **kwargs):        cpt_entity = params['cpt_entity']  # Entity | None        if cpt_entity:            return DataResult(DataGroup(                x=DataItem('CPT X coordinate', cpt_entity.last_saved_params.x_coordinate),                y=DataItem('CPT Y coordinate', cpt_entity.last_saved_params.y_coordinate)            ))        return DataResult(DataGroup())

Bypassing user access restrictions

New in v12.6.0

If not used properly, (confidential) information which SHOULD NOT be accessible to a user may leak (e.g. by including confidential data in a view, the data is visible to users with access to that view). Please consider the following when using this flag:

  • Make sure the app's admin is aware that the code circumvents certain permissions at specific places.
  • Make sure to test the implementation thoroughly, to ensure no confidential data is leaked.

Some applications have user restrictions that limit access to certain entities or entity types. The code of an application inherits the permissions of the user currently running it. This means that, for example, if a user does not have permission to read some entity data, the code will also NOT be able to reach this data through an API call when run by the same user.

In some cases it is desirable to restrict user access to a certain entity but have access within the code nonetheless. An example is the case of a global settings entity that consists of confidential pricing data, which should be inaccessible for users but accessible for the code to calculate costs. For these cases a privileged flag can be used:

  1. Configure viktor.config.toml to make use of the privileged api. This serves as an additional layer of security/awareness.

    enable_privileged_api = true
  2. Use the privileged flag on the relevant API methods.

    from viktor.api_v1 import API...settings_entity = API().get_root_entities(entity_type_names=['Settings'], privileged=True)[0]settings = settings_entity.last_saved_params

Using date-dependent params

New in v12.10.0

API().get_entity_revisions has been introduced in the viktor.api_v1 module, removing the need to use the viktor.api module to obtain entity revisions.

Projects that make use of a database with prices are often invoiced with prices that may change over the years. If such prices are changed and the project is re-opened, the latest revision of the price-database is used by default. However, this could influence resulting data (e.g. a quotation) of an old project that the user would like to review. In such cases it is very helpful to select or specify a date at which the database settings were applicable.

Selecting a date can be implemented in different ways. Note that in case a DateField is used to pick a date you should account for possible UTC offsets. Assume a revision is created in Los Angeles at 17:00 local time (UTC-8), this will result in a UTC time stamp at 1:00 the next day so the latter date should be selected. The user does not know this will happen, so depending on the UTC zone of the user, the possible 1-day fluctuation should be taken into account.


  1. From the backend API, call the revisions of the 'settings' entity (e.g. API().get_entity_revisions(entity_id)).
  2. Get the dates of the revisions, stored under 'created_date' property.
  3. Get the user-specified date.
  4. Since both dates are datetime objects, comparison can easily be done.
  5. Filter the revision that was applicable at the user-specified date.

The last step can vary depending on the way you would like to filter the revisions. For example if multiple revisions are created on the specified date, you can write an algorithm to select the correct revision of that day (e.g. the latest or first created). Make sure to notify the user whether the specified date is within the bounds of the possible revisions or not.


API calls need to be mocked within the context of (automated) testing. There are two approaches you could take to achieve this:

  1. By making use of the mock_API decorator
  2. By isolating API actions in separate methods

Testing by using mock_API

New in v13.3.0
import unittestfrom viktor.testing import mock_APIfrom app.my_entity_type.controller import MyEntityTypeControllerclass TestMyEntityTypeController(unittest.TestCase):    @mock_API()    def test_function(self):        MyEntityTypeController().function(...)

For each method that is called on the API class, the decorator will return mock objects:

  • MockedEntity representing Entity (e.g. when calling API.get_entity())
  • MockedEntityList representing EntityList (e.g. when calling API.get_root_entities())
  • MockedEntityRevision representing EntityRevision (e.g. when calling API.get_entity_revisions())
  • MockedEntityType representing EntityType (e.g. when calling Entity.entity_type)
  • MockedUser representing User (e.g. when calling API.get_current_user())

Each of these mock objects supports the properties and methods of their represented objects, for example, MockedEntity.last_saved_params mimics Entity.last_saved_params.

Assume the function() of the example above performs an API call to retrieve the child entities to count the number of red, green, and blue entities:


class MyEntityTypeController(ViktorController):

def function(self, entity_id):
red, green, blue = 0, 0, 0
children = API().get_entity_children(entity_id)
for child in children:
if child.last_saved_params.color == 'red':
red += 1
if child.last_saved_params.color == 'green':
green += 1
if child.last_saved_params.color == 'blue':
blue += 1
return red, green, blue

By default, the mock_API decorator returns a zero-length MockedEntityList when get_entity_children() is called. This means that, in the example above, function will return (0, 0, 0).

However, the decorator allows you to specify the outcome of each individual method such that we can actually test what would happen if a VIKTOR workspace consists of specific entities. For example, let's pass the entities that get_entity_children() in the example above should return, by providing a sequence of MockedEntity objects in the decorator:

import unittestfrom viktor.testing import mock_API, MockedEntityfrom app.my_entity_type.controller import MyEntityTypeControllerCHILD_ENTITIES = [   MockedEntity(params={'color': 'red'}),   MockedEntity(params={'color': 'green'}),   MockedEntity(params={'color': 'green'}),   MockedEntity(params={'color': 'blue'}),]class TestMyEntityTypeController(unittest.TestCase):    @mock_API(get_entity_children=CHILD_ENTITIES)    def test_function(self):        red, green, blue = MyEntityTypeController().function(...)        self.assertEqual(red, 1)        self.assertEqual(green, 2)        self.assertEqual(blue, 1)

Instead of the default zero-length MockedEntityList, the decorator is now instructed to return the provided entities. Note that in this example the params are passed to MockedEntity, but you can define much more (e.g. parent / children / siblings etc.).

Testing by isolating API actions

Structuring the controller in such a way that each API action is isolated in a separate method makes it easier to maintain, debug, and reuse your code. Assume a controller that looks like this:

...class MyEntityTypeController(ViktorController):    ...    def get_child_params(self, entity_id):        # perform API actions        return child_params    def get_parent_params(self, entity_id):        # perform API actions        return parent_params    @GeometryView("3D model", duration_guess=3)    def visualize(self, params, entity_id, **kwargs):        parent_params = self.get_parent_params(entity_id)        child_params = self.get_child_params(entity_id)        visualization = self.create_visualisation(parent_params, child_params)        return GeometryResult(visualization)    def download_file(self, params, entity_id, **kwargs):        parent_params = self.get_parent_params(entity_id)        download_content = self.generate_download_result(parent_params)        return DownloadResult(download_content)

If the visualize and download_file methods are now to be tested, we can make use of unittests mock.patch.object decorator in combination with mock.MagicMock to control the return value:

import unittestfrom unittest import mockfrom app.my_entity_type.controller import MyEntityTypeController# params dictionariesENTITY_PARAMS = ...PARENT_PARAMS = ...CHILD_PARAMS = ...class TestMyEntityTypeController(unittest.TestCase):            @mock.patch.object(MyEntityTypeController, 'get_child_params', mock.MagicMock(return_value=CHILD_PARAMS))    @mock.patch.object(MyEntityTypeController, 'get_parent_params', mock.MagicMock(return_value=PARENT_PARAMS))    def test_visualise(self):        MyEntityTypeController().visualize(params=ENTITY_PARAMS)    @mock.patch.object(MyEntityTypeController, 'get_child_params', mock.MagicMock(return_value=CHILD_PARAMS))    def test_generate_download_content(self):        MyEntityTypeController().generate_download_content(params=ENTITY_PARAMS)

Mocking the deserialized params

The VIKTOR platform deserializes the raw params, such that they will enter the app code in their intuitive format. For example, an EntityOptionField will return an Entity object in the params instead of an entity_id. However, when you are using raw params in your tests (e.g. a JSON file), you will need to deserialize the params yourself. This is necessary when any of the following fields is used in the parametrization of the corresponding entity type:

  • DateField
  • EntityOptionField
  • ChildEntityOptionField
  • SiblingEntityOptionField
  • EntityMultiSelectField
  • ChildEntityMultiSelectField
  • SiblingEntityMultiSelectField
  • GeoPointField
  • GeoPolylineField
  • GeoPolygonField
  • FileField
  • MultiFileField

For example, when the parametrization consists of a NumberField, ChildEntityOptionField, and a FileField, the raw params could look like:

params = {
'number': 1,
'entity': 2, # corresponds to entity id
'file': 3, # corresponds to file resource id

Let's assume function() to perform an API call to

  • retrieve the last_saved_params of the selected child entity
  • retrieve the content of the selected file

class MyEntityTypeController(ViktorController):

def function(self, params):
child_params = params.entity.last_saved_params
file = params.file.file

The raw integers of params_dict should be converted to their corresponding mock objects (i.e. deserialized), in order for function() to succeed. There are two ways to achieve this:

  1. By defining a dictionary consisting of the mock objects manually

    from viktor import File
    from viktor.testing import MockedEntity, MockedFileResource

    params = {
    'number': 1,
    'entity': MockedEntity(name="My Entity"),
    'file': MockedFileResource(file=File.from_data("content"), filename="file.txt"),
  2. By using the mock_params function, providing a JSON file consisting of the raw params along with the Parametrization and (optionally) mocked resources

    from viktor import File
    from viktor.testing import MockedEntity, MockedFileResource, mock_params

    params = mock_params(
    params=File.from_path("path to JSON file"),
    entities={2: MockedEntity(name="My Entity")},
    file_resources={3: MockedFileResource(file=File.from_data("content"), filename="file.txt")},

The function() can then be tested by providing the deserialized params:


class TestMyEntityTypeController(unittest.TestCase):

def test_function(self):