"Attempt to modify the model outside of transaction": zo los je het op
De foutmelding die iedereen een keer ziet. Wat hij betekent, hoe je hem in drie regels oplost, en de minder voor de hand liggende varianten.
Kort antwoord: je script verandert iets in het model (bijvoorbeeld met Set) zonder dat er een transaction open is. Zet de wijziging in een with revit.Transaction("naam"):-blok, of start zelf een Transaction en sluit hem af met Commit(). Lezen mag zonder transaction, wijzigen nooit.
De foutmelding
In pyRevit ziet het uitvoervenster er ongeveer zo uit. De precieze opmaak verschilt per versie en engine; de laatste regel is altijd dezelfde:
IronPython Traceback:
Traceback (most recent call last):
File "C:\...\MijnKnop.pushbutton\script.py", line 9, in <module>
Exception: Attempt to modify the model outside of transaction.
Script Executor Traceback:
Autodesk.Revit.Exceptions.ModificationOutsideTransactionException: Attempt to modify the model outside of transaction.Wat het betekent
Revit wil dat elke wijziging aan een model in een transaction gebeurt: een afgebakende stap met een naam. Die stap kun je met Ctrl+Z ongedaan maken, en als er halverwege iets misgaat, draait Revit de hele stap terug. Zo blijft je model altijd heel. Lezen verandert niets en mag dus zonder transaction; elke wijziging, ook één parameter, niet.
Dit is de foutieve versie. Hij zet een opmerking op een leiding, maar zonder transaction:
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
leiding = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.FirstElement())
p = leiding.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)
p.Set("gecontroleerd")Autodesk.Revit.Exceptions.ModificationOutsideTransactionException: Attempt to modify the model outside of transaction.De oplossing in drie regels
Met pyRevit zet je de wijziging in een with-blok. Dat start de transaction, voert de wijziging uit en sluit hem netjes af:
from pyrevit import revit, DB
leiding = (DB.FilteredElementCollector(revit.doc)
.OfCategory(DB.BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.FirstElement())
p = leiding.get_Parameter(DB.BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)
with revit.Transaction("Opmerking invullen"):
p.Set("gecontroleerd")
print(p.AsString())gecontroleerdZonder pyRevit gebruik je Transaction(doc, "naam") met Start(), Commit() en bij een fout RollBack(). Een volledig voorbeeld staat in Revit-parameters lezen en wijzigen.
Varianten die je tegenkomt
De wijziging staat net buiten het blok
Python kijkt naar het inspringen. Staat de regel met Set niet ingesprongen onder de with, dan is de transaction al dicht als hij wordt uitgevoerd:
from pyrevit import revit, DB
leidingen = (DB.FilteredElementCollector(revit.doc)
.OfCategory(DB.BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.ToElements())
with revit.Transaction("Opmerking invullen"):
eerste = leidingen[0]
eerste.get_Parameter(DB.BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).Set("klaar")Autodesk.Revit.Exceptions.ModificationOutsideTransactionException: Attempt to modify the model outside of transaction.Een functie die later wordt aangeroepen
Je schrijft een hulpfunctie die een parameter zet, en roept die aan vanuit een plek zonder transaction. De fout wijst dan naar de regel in de functie, niet naar de aanroep. Spreek daarom af wie de transaction opent: of de functie zelf, of altijd de code die hem aanroept. In een eigen library is "de aanroeper" meestal het handigst, zodat je meerdere wijzigingen in één stap kunt bundelen.
Een transaction op het verkeerde document
Een transaction hoort bij één document. Werk je met meerdere modellen tegelijk, dan moet de transaction geopend zijn op het document dat je wijzigt.
Eén transaction tegelijk
Per document kan er maar één transaction tegelijk open zijn. Open je er binnen een revit.Transaction nog een met de pyRevit-hulp, dan werkt die binnen de eerste. Voor ingewikkelder gevallen kent de Revit-API ook SubTransaction (een stap binnen een transaction) en TransactionGroup (meerdere transactions als één undo-stap). Die heb je voor gewone knoppen zelden nodig.
En als er in het blok iets misgaat?
Dan draait pyRevit alles terug, schrijft een regel in het logboek en laat de fout gewoon zien. Je model blijft dus heel:
ERROR [pyrevit.revit.db.transaction] Error in Transaction Context. Rolling back changes. | ZeroDivisionError:division by zero