When you generate contracts, reports, or manuals in bulk, the same handful of values keeps appearing throughout each file: a version number, a generation date, a customer name, a reviewer. Rewriting them with plain string replacement means missing occurrences, and it leaves no single place to control the value — once somebody edits the template by hand, the replacement logic stops matching.
Word's built-in document variables were designed for exactly this. A document variable is a key–value pair stored inside the file, and the body text references it through a DOCVARIABLE field: store "2.4" in the variable and the document displays "2.4"; change the variable to "3.1" and refresh the fields, and every reference in the document updates at once. In effect, the content becomes a template that a script can fill.
This article uses Spire.Doc for Python to walk through the full life cycle of a document variable: adding and referencing, reading, updating, removing, and the field-refresh timing that is easiest to overlook.
Setting Up the Environment
Install Spire.Doc and import the modules:
pip install Spire.Doc from spire.doc import * from spire.doc.common import * The variable collection hangs off the document object and is reached through document.Variables, an instance of VariableCollection.
Adding a Variable and Referencing It
Adding a variable takes two steps: store the key–value pair with Variables.Add(name, value), then insert a DOCVARIABLE field that points at it with AppendField.
from spire.doc import * from spire.doc.common import * document = Document() section = document.AddSection() # A variable that is referenced from the body text. paragraph = section.AddParagraph() paragraph.AppendText("Contract version: ") paragraph.AppendField("Version", FieldType.FieldDocVariable) paragraph.AppendText(", generated on ") paragraph.AppendField("BuildDate", FieldType.FieldDocVariable) document.Variables.Add("Version", "2.4") document.Variables.Add("BuildDate", "2026-11-13") document.Variables.Add("Reviewer", "QA team") document.IsUpdateFields = True document.SaveToFile("DocumentVariables.docx", FileFormat.Docx2013) document.Close() Three details are worth calling out:
- The first argument of
AppendField("Version", FieldType.FieldDocVariable)is the variable name, and the field code is written asDOCVARIABLE Version. The same variable can be referenced as many times as you like. - Variable names are case-sensitive.
Versionandversionare two separate entries, so a wrong capitalisation leaves the reference without a value. -
document.IsUpdateFields = Truemakes Spire refresh all field results when the document is saved. Without it, the old result stays in the file even after the variable has been changed.
That last point is the trap most often hit while debugging this kind of script: without the refresh, the body keeps displaying the value the variable had last time, even though the variable itself has already changed.
Reading Variables from a Document
VariableCollection supports lookup by index and by name:
from spire.doc import * document = Document() document.LoadFromFile("DocumentVariables.docx") print("count:", document.Variables.Count) for i in range(document.Variables.Count): name = document.Variables.GetNameByIndex(i) value = document.Variables.GetValueByIndex(i) print(f"[{i}] {name} = {value}") print("by name:", document.Variables["Version"]) document.Close() -
Countis the number of variables and the natural loop boundary. -
GetNameByIndex(i)andGetValueByIndex(i)return the name and value at a given position, which suits iteration. -
document.Variables["Version"]fetches a value by name and is the better choice when the name is already known.
Note that reading a name that does not exist does not raise an error; the collection returns None instead. Automation code should therefore check that return value explicitly rather than rely on an exception to detect a missing variable.
Updating a Value
Changing a stored value needs a little care. Add() refuses to store a duplicate name and throws an Argument_AddingDuplicateWithKey exception, and the Python binding does not support item assignment such as document.Variables["Version"] = "3.1" (that raises TypeError). The reliable pattern is to remove the old entry first and then add the new one:
from spire.doc import * document = Document() document.LoadFromFile("DocumentVariables.docx") # Add() rejects a duplicate name, so remove the old entry before adding the new value. document.Variables.Remove("Version") document.Variables.Add("Version", "3.1") print("updated value:", document.Variables["Version"]) document.IsUpdateFields = True document.SaveToFile("DocumentVariablesUpdated.docx", FileFormat.Docx2013) document.Close() Reading document.Variables["Version"] afterwards returns "3.1", and the saved document shows the DOCVARIABLE field displaying "3.1" as well.
If IsUpdateFields = True is left out, the saved file still displays the old value "2.4" — the variable itself was updated, but the text in the body was never refreshed.
Removing a Variable
Use Remove(name) to drop a variable:
from spire.doc import * document = Document() document.LoadFromFile("DocumentVariablesUpdated.docx") document.Variables.Remove("BuildDate") print("count after remove:", document.Variables.Count) document.IsUpdateFields = True document.SaveToFile("DocumentVariablesRemoved.docx", FileFormat.Docx2013) document.Close() The count drops from 3 to 2. The DOCVARIABLE field itself stays in the document, but after the refresh it displays nothing: the variable is gone, so the place that referenced it becomes blank.
Calling Remove() with a name that does not exist raises nothing and simply returns None, which keeps bulk cleanup code simple.
Where the Variables Live
Document variables are not part of the body content. They are written into word/settings.xml inside the docx package, in exactly the structure Word itself uses:
<w:docVars> <w:docVar w:name="Version" w:val="2.4" /> <w:docVar w:name="BuildDate" w:val="2026-11-13" /> <w:docVar w:name="Reviewer" w:val="QA team" /> </w:docVars> Two consequences follow from this. First, variables stay separate from the text, so changing one never disturbs the layout. Second, any tool that can parse a docx can read these pairs, which makes it straightforward for an external system to check whether a generated document carries the values it is supposed to carry.
Practical Tips
- Treat the document as a template: keep version numbers, dates, and similar volatile values in variables, and let the script only write variables and refresh the fields. This is far more reliable than replacing text throughout the body.
- Check before reading: a lookup by name returns
Noneinstead of raising, so test the result when a variable may be absent. - Remove before you update:
Add()throwsArgument_AddingDuplicateWithKeyon a duplicate name, so removing the old entry first is the only dependable way to overwrite a value. - Refresh fields before saving:
document.IsUpdateFields = Trueis what keeps the displayed text in step with the variables. - Pick one naming convention: names are case-sensitive, so stick to camel case or upper case and avoid mixing
Versionwithversion. - Release resources: call
document.Close()once the document is no longer needed.
Conclusion
Document variables turn a Word document into a fillable template. Variables.Add() stores the key–value pair, AppendField with FieldType.FieldDocVariable links it into the body, Count, GetNameByIndex, GetValueByIndex, and name lookup read what is already there, "remove then add" updates a value, and Remove() clears the entries you no longer need.
Only two behaviours really need attention: Add() rejects a duplicate name, so an overwrite must remove the old entry first, and IsUpdateFields = True decides whether the body text follows the variable. With those two settled, one template can reliably drive bulk document generation.


