Setting fields in schema

import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
clr.AddReference('RevitAPIUI')

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI.Selection import ObjectType
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
import System

schemaId = System.Guid("   ADD A GUID HERE ... ")

try:
    doc = DocumentManager.Instance.CurrentDBDocument
    uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

    TransactionManager.Instance.EnsureInTransaction(doc)

    wallRef = uidoc.Selection.PickObject(ObjectType.Element, "Select a wall")
    wall = doc.GetElement(wallRef.ElementId)

    schemaId = System.Guid(schemaId)
    schema = ExtensibleStorage.Schema.Lookup(schemaId)

    if schema is None:
        builder = ExtensibleStorage.SchemaBuilder(schemaId)
        builder.SetReadAccessLevel(ExtensibleStorage.AccessLevel.Public)
        builder.SetWriteAccessLevel(ExtensibleStorage.AccessLevel.Public)
        builder.SetSchemaName("VeryFirstAttemptAtThis")
        builder.SetDocumentation("Data store for my very first attempt")
        schema = builder.Finish()
        BuildInfo = "Schema created successfully."
    else:
        BuildInfo = "Schema already exists."
    

except Exception as e:
    BuildInfo = f"An error occurred: {str(e)}"

#Now the schema exists. So do things with it...
builder = ExtensibleStorage.SchemaBuilder(schemaId)

# Create field1
fieldBuilder1 = builder.AddSimpleField("WallLength", XYZ)
fieldBuilder1.SetSpec(SpecTypeId.Length)  # https://www.revitapidocs.com/2023/87de2c69-a5e8-40e3-3d7a-9b18f1fda03a.htm
fieldBuilder1.SetDocumentation("this is the documentation here") 

# Create field2
fieldBuilder2 = builder.AddSimpleField("WallNumber", str)
#fieldBuilder2.SetSpec(ForgeTypeId())

### Set value of field and attach to objects in Revit### 
try:
    entity = ExtensibleStorage.Entity(schema)
    fieldName = "WallLength"   
    field = schema.GetField(fieldName)
    
    if field is not None:
        entity.Set[XYZ](field, XYZ(2, 0, 0), UnitTypeId.Millimeters)
        wall.SetEntity(entity)
    else:
        output = "Field not found: " + fieldName

except Exception as e:
    output = str(e)  # error  message 
TransactionManager.Instance.TransactionTaskDone()
#TransactionManager.Instance.EnsureInTransaction(doc)

OUT = BuildInfo, "moose!" , output

So the schema built, the moose is output… however i’m getting an error on the field.

image

I am wondering why. Is it because the way I’ve tried to do it the fields would have to be created at the same time as the Schema?
If that’s the case, how do I add fields after the Schema is created?

I think you may need to finish the schema builder before you access content, but it has been awhile.

Try adding builder.Finish() in advance of calling for the field.

I have… It’s in the loop to create the schema

Ah. Good catch.

I’ll have a look at my sample tomorrow if I can find the time. :slight_smile:

1 Like

Thanks. I’m using Revit 2023.

1 Like

I’ve redone this and it seems you can’t add fields after you’ve built the schema… Which would answer this question.

Anyone confirm this please?

Confirmed - creation is a one time thing.

2 Likes

Hi

It is possible to replace stored values.
Here is an example with a dictionary, where you can add or replace several keys, values

import System
import sys
import clr

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import specify namespace
from Autodesk.Revit.DB.ExtensibleStorage import Schema, SchemaBuilder, AccessLevel, Entity, Field

#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary, IDictionary

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

def create_schema(schema_guid):
	schema_builder = SchemaBuilder(schema_guid)
	schema_builder.SetReadAccessLevel(AccessLevel.Public)
	schema_builder.SetWriteAccessLevel(AccessLevel.Public)
	schema_builder.SetSchemaName("DataSchema")
	schema_builder.SetVendorId("XXXX")
	schema_builder.SetDocumentation("Schema to store Data")
	#
	fieldBuilder_dict = schema_builder.AddMapField("Dict_Data", System.String, System.String)
	fieldBuilder_dict.SetDocumentation("A Dictionary to store any value String data")
	#
	schema = schema_builder.Finish()
	return schema

def add_schema_instance(schema, rvt_element, pydict = {}):
	#
	# get Field from DataStorage
	fieldDict = schema.GetField("Dict_Data")
	# set schema and store some datas
	schema_instance = Entity(schema)
	# store Datas Element in Dictionary
	stringMap = Dictionary[System.String, System.String](pydict)
	schema_instance.Set[IDictionary[System.String, System.String]](fieldDict.FieldName, stringMap)
	rvt_element.SetEntity(schema_instance)
	return rvt_element

def read_DataStorage(schema, rvt_element, searchValue = None):
	"""
	return DataStorage Value by Name of Field if failed search in Dictionnary 'Dict_Data'
	Dict_Data -> Store and Read Datas in an IDictionnary
	"""
	ent = rvt_element.GetEntity(schema)
	if ent.IsValid():
		i_Dict = ent.Get[IDictionary[System.String, System.String]]("Dict_Data")
		mydict = dict(zip(i_Dict.Keys, i_Dict.Values))
		if isinstance(searchValue, (str, System.String)):
			return mydict.get(searchValue)
	return None
 
rvt_element = UnwrapElement(IN[0])
# create data to store with a dictionary {String : String, String : String}
data_dict = {"rvt_Id" : rvt_element.Id.ToString(), "rvt_uniqId" : rvt_element.UniqueId}
#
TransactionManager.Instance.EnsureInTransaction(doc)
#
schema = create_schema(System.Guid("18163872-127c-4615-9cc3-47f16726b6de"))
doc.Regenerate()
# store 1st data
add_schema_instance(schema, rvt_element, data_dict)
# add new Data and store again
data_dict["Dynamo"] = "BIM"
add_schema_instance(schema, rvt_element, data_dict)

TransactionManager.Instance.TransactionTaskDone()

OUT = []
for i in ["rvt_Id", "Dynamo", "missingKey"]:
    OUT.append([i, read_DataStorage(schema, rvt_element, searchValue = i)])
4 Likes

Beautiful!! I was wondering about using a dictionary… Currently I’ve been playing with comma separated strings.

Happy Birthday btw :smiley:

Is it forum birthday or real birthday?

Real :blush:

Thanks you

2 Likes

I have just discovered you can add the same schema to multiple objects in Revit…

…But have different fields in each?!

How the moose does that work? Are they different dictionaries stored in the same schema?

the schema is the same, but the values can be different, and in the case here of a dictionary the keys/values can be different

1 Like

Oooo… so the schema is the class… and the entities are just instantiations of that class.

I see… Interesting!! Also, incredibly confusing!

Can I add the schema to absolutely anything? Can I add it to a system parameter or a project parameter maybe?

entity schema can be added only on DB.Element(s)

1 Like

If I attach the schema to a database element… but then someone deletes that element… are all my fields wiped clean?
Is there any way of retrieving that data?

I think no, maybe by searching in previous model version (cloud model)

1 Like