Managing Document Variables in Word Documents Using Python

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 as DOCVARIABLE Version. The same variable can be referenced as many times as you like.
  • Variable names are case-sensitive. Version and version are two separate entries, so a wrong capitalisation leaves the reference without a value.
  • document.IsUpdateFields = True makes 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() 
  • Count is the number of variables and the natural loop boundary.
  • GetNameByIndex(i) and GetValueByIndex(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 None instead of raising, so test the result when a variable may be absent.
  • Remove before you update: Add() throws Argument_AddingDuplicateWithKey on a duplicate name, so removing the old entry first is the only dependable way to overwrite a value.
  • Refresh fields before saving: document.IsUpdateFields = True is 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 Version with version.
  • 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.