# Save/restore component properties to DB

**URL:** <https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383>\
**Category:** Ignition\
**Tags:** perspective\
**Created:** [April 4, 2024, 10:07pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383 "2024-04-04T22:07:48Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![David\_Nicks](https://avatars.discourse-cdn.com/v4/letter/d/8491ac/32.png) [@David\_Nicks](https://forum.inductiveautomation.com/u/David_Nicks)\
**Post date:** [April 4, 2024, 10:07pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/1 "2024-04-04T22:07:48Z")

</div>

v8.1

Trying to build a couple functions that will allow users to save runtime configurations (e.g. table filtering/columns/sorting/etc) into a DB so they can be sticky between sessions. I have a decent working prototype using `copy.deepcopy()` in a view's Startup & Shutdown scripts on a single table component.

Now I'd like to move this to a project function so it can be used in many places, something like `saveProp(comp,prop)` and `restoreProp(comp,prop)`, where the function takes care of building the named query parameters out of the user credentials and supplied properties, `comp` is an object referencing the component whose properties are being saved/restored, and `prop` is a string value pointing to the property to save, e.g. `props.filters` for a table.

I've been using [Scripting reference parent property with parameter - Ignition - Inductive Automation Forum](https://forum.inductiveautomation.com/t/scripting-reference-parent-property-with-parameter/68513) for ideas...

I have a few questions/problems:

1. I'm hoping to keep script execution on the client as much as possible, since our client count may get pretty large and I don't want to burden the gateway. Are project library scripts executed on the client or the gateway?
2. When my test view calls the project function, I'm getting an error:

> AttributeError: 'com.inductiveautomation.perspective.gateway.script' object has no attribute 'getattr'

This is the project function:

```auto
def propGet (comp, prop):
	props = comp.getattr(comp, prop)
	print "props: %s" %(props)

```

This is the calling script, attached to a button on the same view as an alarm status table:

```auto
def runAction(self, event):
	comp = self.getSibling("AlarmStatusTable")
	system.perspective.print("comp: %s" %(comp))
	project.functions.propGet(comp, 'props.filters.active.states')

```

The output console shows this from the calling script:

> comp: com.inductiveautomation.perspective.gateway.script.ComponentModelScriptWrapper$SafetyWrapper@153dd4d

Not sure I understand why the component reference includes `gateway.script` when it's coming from an event script on a button.

Why is `getattr()` failing?

.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [April 4, 2024, 10:13pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/2 "2024-04-04T22:13:01Z")

</div>

`getattr()` is a standalone python method. You should be using it as `props = getattr(comp, prop)` And at that point you don't really need your custom method, unless you want the logging of the value.

> [@David\_Nicks](#):
>
> I'm hoping to keep script execution on the client as much as possible, since our client count may get pretty large and I don't want to burden the gateway. Are project library scripts executed on the client or the gateway?

This is perspective, so always gateway. Nothing in perspective is executed client side.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 4, 2024, 10:33pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/3 "2024-04-04T22:33:53Z")

</div>

> [@ryan.white](#):
>
> Nothing in perspective is executed client side.

Well, no scripts or expressions you author. Lots of client side code is running for components, but it's generally speaking not the "source of truth" for component state.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 5, 2024, 12:31am UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/4 "2024-04-05T00:31:42Z")

</div>

> [@David\_Nicks](#):
>
> since our client count may get pretty large and I don't want to burden the gateway

Plan on a beefy gateway, and almost certainly a frontend-backend split, where drivers and tag history are handled on a gateway that **doesn't** have any Perspective load.

---

<div class="post-metadata">

**Author:** ![David\_Nicks](https://avatars.discourse-cdn.com/v4/letter/d/8491ac/32.png) [@David\_Nicks](https://forum.inductiveautomation.com/u/David_Nicks)\
**Post date:** [April 8, 2024, 8:40pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/5 "2024-04-08T20:40:59Z")

</div>

Ok, boneheaded mistake on my part, thanks for pointing out the bad syntax on `getattr()`.

Follow-up question since I'm now running into other issues. I am able to use getattr() and save component properties to the DB. However, when I attempt to restore the properties from the DB to the component, I'm getting errors. I'm using the recommendation from @pturmel in [this thread](https://forum.inductiveautomation.com/t/function-to-set-component-property/15074/2) to try both `setattr()` and `setPropertyValue` and it looks like both are failing.

project script:

```auto
def testwriteprops(comp):
	logger = system.util.getLogger("project.functions.testwriteprops")
	logger.trace("test write comp: %s" %(str(comp)))
	prop = 'props.filters.active'
	value = {u'priorities': {u'high': True, u'critical': True, u'low': True, u'diagnostic': False, u'medium': True}, u'text': u'', u'conditions': {u'provider': u'', u'source': u'', u'displayPath': u''}, u'results': {u'data': [], u'enabled': False}, u'states': {u'activeAcked': True, u'clearAcked': False, u'clearUnacked': True, u'activeUnacked': True}}
	try:
		logger.trace("trying setattr")
		setattr(comp,prop,value)
		logger.trace("used setattr")
	except AttributeError:
		logger.trace("trying setPropertyValue")
		comp.setPropertyValue(prop,value)
		logger.trace("used setPropertyValue")
	logger.trace("filters saved")

```

calling script on a button:

```auto
def runAction(self, event):
	comp = self.getSibling("AlarmStatusTable")
	project.functions.testwriteprops(comp)

```

Log output:

> Error running action 'component.onActionPerformed' on testview@D/root/PasteButton: Traceback (most recent call last): File "function:runAction", line 26, in runAction File "module:project.functions", line 738, in testwriteprops AttributeError: 'com.inductiveautomation.perspective.gateway.script' object has no attribute 'setPropertyValue'

Seems like I'm not doing a proper job of retrieving the component reference, but I don't know why. The logger shows the value of `comp` as `com.inductiveautomation.perspective.gateway.script.ComponentModelScriptWrapper$SafetyWrapper@2f507ffb`

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 8, 2024, 9:12pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/6 "2024-04-08T21:12:16Z")

</div>

The safety wrapper is a Perspective thing. Most of those old techniques are Vision-only.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [April 8, 2024, 9:15pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/7 "2024-04-08T21:15:48Z")

</div>

From the SDK docs for [ComponentModelScriptWrapper](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/perspective/gateway/script/ComponentModelScriptWrapper.html), it notes that the safety wrapper class 'Delegates to the actual component, this prevents the scripting system from being able to call everything on ComponentModel, like startup(), shutdown, etc.'

It looks like there is a [PropetyTree](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.25/com/inductiveautomation/perspective/gateway/property/PropertyTree.html) class you get from the component that seems to have methods to write to different properties on the component.

What is stopping you from writing these configs to a session custom property or a view custom property on startup? It would be simpler, as you would just dump all the configs into there (separating into additional properties as need) and then just make sure the components that need the configuration are bound to their associated property in that top level configuration.

---

<div class="post-metadata">

**Author:** ![David\_Nicks](https://avatars.discourse-cdn.com/v4/letter/d/8491ac/32.png) [@David\_Nicks](https://forum.inductiveautomation.com/u/David_Nicks)\
**Post date:** [April 8, 2024, 9:57pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/8 "2024-04-08T21:57:53Z")

</div>

Thanks for the lead. I've never even seen the SDK before.

The merge method looks promising...will have to experiment. Is there additional documentation on the classes in the SDK (examples, etc.) that would include the PropertyTree class?

If I understand you correctly, you're suggesting I create custom properties on the session object and then bind the relevant view component properties to the custom session properties. That would keep user preferences sticky during a session, but then they'd lose them on a new session - correct?

The app we're building will have a number of different tables that users will customize (e.g. filters, columns shown/hidden, column order, width, etc.) based on their role and location, shift, coverage area, etc. In some cases we can derive this from user data, but there's also a lot of personal preference going on and we want to provide users with a way of saving a snapshot of their customizations. We don't want them to have to go through all the clicks to restore their customizations every time they start a new session.

Maybe it's easier to save & restore the session custom properties to a DB than each of the table properties?

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [April 8, 2024, 10:13pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/9 "2024-04-08T22:13:23Z")

</div>

> [@David\_Nicks](#):
>
> user preferences sticky during a session, but then they'd lose them on a new session - correct?

Correct. To make it sticky, save/load from the DB.

> [@David\_Nicks](#):
>
> Maybe it's easier to save & restore the session custom properties to a DB than each of the table properties?

I'd say this is much easier and more transparent than diving into using SDK functions, especially if you get another engineer who knows nothing about the project/customization data. (That's my opinion anyways). It would also essentially 'live' in only one or two places, the session startup handler and the user id value change event, as opposed to every component that needed some sort of customization.

You could have a structure something along the lines of:

```auto
personalization
├ Tables
│ ├ TableTypeA
│ │ ├ Columns
│ │ ├ Filters
│ │ └ Rows
│ └ TableTypeB
│ └ Columns
├ Graphs
│ ├ GraphTypeA
│ └ GraphTypeB
└ ETC...

```

All your script would need to do is poll the DB for the provided user and transform the data into the correct structure and place that structure into the session property. Simple 3 line script for the event.

```auto
	databasePersonalizationData = system.db.runNamedQuery("GetPersonalization", {"OperatorId": operatorId})
	personalizationObject = personalizationDataToObject(databasePersonalizationData)
	session.custom.personalization = personalizationObject

```

with `personalizationDataToObject` doing all the structure transformation.

---

<div class="post-metadata">

**Author:** ![David\_Nicks](https://avatars.discourse-cdn.com/v4/letter/d/8491ac/32.png) [@David\_Nicks](https://forum.inductiveautomation.com/u/David_Nicks)\
**Post date:** [April 8, 2024, 10:20pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/10 "2024-04-08T22:20:15Z")

</div>

I like that approach. Time to pivot.

Thanks for the fresh angle!

---

<div class="post-metadata">

**Author:** ![Ryan\_Ahlstrom](https://avatars.discourse-cdn.com/v4/letter/r/ecb155/32.png) [@Ryan\_Ahlstrom](https://forum.inductiveautomation.com/u/Ryan_Ahlstrom)\
**Post date:** [April 9, 2024, 2:25pm UTC](https://forum.inductiveautomation.com/t/save-restore-component-properties-to-db/88383/11 "2024-04-09T14:25:19Z")

</div>

We have had some great success utilizing MongoDB for prop and template storage. We binded the props for the components we wanted to store to session custom properties and then have features added to create templates and keep user settings between sessions on perspective.
