Edit family within model document

I am trying to create a script to do the following:

  • read family data from excel including: (Family Name, Location in Library and Family Parameters List).

  • For each family name:

  1. Check if family is loaded if not load it from library.
  2. Replace family parameters with shared parameters.
  3. Reload the modified family again into the model.

Her is the python code:

# Load the Python Standard and DesignScript Libraries
import sys
import clr
import os.path

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# The inputs to this node will be stored as a list in the IN variables.
allFamilyNames = IN[0]
allfamilyLocationInLibrary = IN[1]
allFamilyParameterNamesLists = IN[2]

# Place your code below this lineclass FamilyLoadOptions(IFamilyLoadOptions):
class FamilyLoadOptions(IFamilyLoadOptions):
	def OnFamilyFound(familyInUse, overwriteParameterValues):
		overwriteParameterValues = True
		return True
	def OnSharedFamilyFound(sharedFamily, familyInUse, source, overwriteParameterValues):
		source = FamilySource.Family
		overwriteParameterValues = True
		return True

def CreateSharedParameterExternalDefinition(definitionFile ,groupName, parameterName, parameterType):
	try:
		definitionGroup = next(dg for dg in definitionFile.Groups if dg.Name == groupName)
	except StopIteration:
		definitionGroup = definitionFile.Groups.Create(groupName)
	try:
		definition = next(d for d in definitionGroup.Definitions if d.Name == parameterName)
	except StopIteration:
		externalDefinitionCreationOptions =  ExternalDefinitionCreationOptions(parameterName, parameterType)
		definition = definitionGroup.Definitions.Create(externalDefinitionCreationOptions)
	return definition
	
metricLibraryPath = clr.Reference[str]()
currentDBDocument = DocumentManager.Instance.CurrentDBDocument
currentDBDocumentApplication = currentDBDocument.Application;
sharedParameterFile = currentDBDocumentApplication.OpenSharedParameterFile()
currentDBDocumentApplication.GetLibraryPaths().TryGetValue("Metric Library", metricLibraryPath)
metricLibraryPath = metricLibraryPath.Value
if metricLibraryPath is None or len(metricLibraryPath) > 0:
	metricLibraryPath = "C:\\ProgramData\\Autodesk\\RVT 2021\\Libraries\\English\\" 
for singleFamilyName, singleFamilyLocationInLibrary, singleFamilyParameterNamesList in zip(allFamilyNames, allfamilyLocationInLibrary, allFamilyParameterNamesLists):
	family = clr.Reference[Family]()
	try:
		family = next(f for f in FilteredElementCollector(currentDBDocument).OfClass(Family) if f.Name == singleFamilyName)
	except StopIteration:
		familyFilePathName = metricLibraryPath + singleFamilyLocationInLibrary + singleFamilyName + ".rfa"
		currentDBDocument.LoadFamily(familyFilePathName, family)
		family = family.Value
	familyDocument = currentDBDocument.EditFamily(family)
	for familyParameterName in singleFamilyParameterNamesList:
		familyParameter = next(fp for fp in familyDocument.FamilyManager.GetParameters() if fp.Definition.Name == familyParameterName)
		externalDefinition = CreateSharedParameterExternalDefinition(sharedParameterFile, "General", familyParameter.Definition.Name, familyParameter.Definition.ParameterType)
		TransactionManager.Instance.EnsureInTransaction(familyDocument)
		familyDocument.FamilyManager.RenameParameter(familyParameter, "tempName")
		familyDocument.FamilyManager.ReplaceParameter(familyParameter, externalDefinition, BuiltInParameterGroup.PG_GEOMETRY, False)
		TransactionManager.Instance.TransactionTaskDone()
		familyDocument.LoadFamily(currentDBDocument, FamilyLoadOptions())

# Assign your output to the OUT variable.
OUT = 0

I am getting
Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
Traceback (most recent call last):
File “”, line 62, in
Exception: The document is currently modifiable! Close the transaction before calling EditFamily.

What is wrong with my code?

Try wrapping the entire code within a single transaction. I find that usually causes less trouble than repeatedly opening a new transaction within a loop.

The transaction only on the family document not on the model document.
can not warp all code since EditFamily need no transaction.
My problem is that warning is telling me to close the transaction while there is no open transaction at line 62 where the error is

When you hit run in Dynamo a transaction is opened on the active project, which may be your issue. Try forcing all transactions to close prior to starting the effort in the families.

1 Like

We have no idea which line is 62 so we’re guessing at where the error is occurring. I’ve noticed sometimes the line returned in the error isn’t actually the line where the error is occurring, but it should be close. It also sounds like the transaction isn’t closing. Even after closing the transaction, it still needs time to commit, which may not be happening.

My original point though was with this loop:

  • for familyParameterName in singleFamilyParameterNamesList:

This is where your transaction is occurring and the way you have it setup it’s opening and closing a new transaction for every parameter. You should be able to move it up a loop and have one transaction per family. Generally, the fewer transactions you have the better.

This works I add:

transaction = TransactionManager.Instance
transaction.ForceCloseTransaction()

before calling edit family, but then I got error at calling LoadFamily to reload the family again in the source document

I make a transaction on each family document to replace the parameters for each.
How can I use on transaction in this scenario??

You’ll need to close the transaction you did in the family document, and then start a new one in the project.

Usually methods only require an active transaction, not their own unique own. This means that you can have multiple methods run within the same transaction. Just move the transaction outside of the parameter loop and into the main family loop. This will update all the parameters for a single family within one transaction. As Jacob mentioned, you will still need a separate transaction for loading the families into the project.

Not sure if the transaction will propagate across the documents to allow creation of the shared parameters. Very nice if true.

I might be missing something - admittedly, I only skimmed through the code, but isn’t it loading multiple parameters into a family for multiple families? If each family is using a single SPF for the parameters, then you should be able to load all the parameters for a single family within a single transaction, right?

This is a good idea and making sense moving the transaction outside of the parameter loop and inside the family loop.
Thank you
But as I mentioned above after adding:
transaction.ForceCloseTransaction()
EditFamily method executes with no problems now the problem come at calling of method LoadFamily it produce: Exception: Family loading failed.

I checked the API Documentation for LoadFamily Exceptions and found the following:

1- Autodesk.Revit.Exceptions ArgumentNullException: Thrown when the input argument-“targetDocument” or “familyLoadOptions”-is a null reference.
(This not the case, I checked those object just before calling LoadFamily Method).

2- Autodesk.Revit.Exceptions InvalidOperationException: Thrown:
* when the current document is not a family document.
(This not the case, I checked this object just before calling LoadFamily Method).
or:
* when the target document is modifiable.
(This not the case, I checked IsModifiable property just before calling LoadFamily Method).
or:
* doesn’t support load of this kind of families.
(This not the case, I am reloading a family into it’s original source document).
or:
* the load was cancelled due to a conflict and a False return from one of the interface methods.
(I do not know what this means).
or:
* this document is currently in a read-only state.
(This not the case, I checked IsReadOnly property just before calling LoadFamily Method).

3- Autodesk.Revit.Exceptions ForbiddenForDynamicUpdateException: Thrown if this method is called during dynamic update.
(I think this not the case, as I am not implementing dynamic update).

so I think the remaining reason is: “conflict and a False return from one of the interface methods”
Any one know what this means?

Thank You In advance

hello @ahmadmahrous_1
you have missing the “self” instance in FamilyLoadOptions class and for overriding parameters read this post Reload Family and overwrite parameter values - #11 by c.poupin

That’s it Thank you. Now the script working good