or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dynamic-states.mdexceptions.mdfield-types.mdindex.mdmodel-mixins.mdsignals.mdtransitions.mdvisualization.md

visualization.mddocs/

0

# Visualization and Management

1

2

Django management command for creating GraphViz visualizations of state machine transitions, helping developers understand and document complex state workflows.

3

4

## Capabilities

5

6

### graph_transitions Management Command

7

8

Django management command that generates GraphViz dot files visualizing FSM transitions. The command can create visual representations of state machines as directed graphs showing states as nodes and transitions as edges.

9

10

```python { .api }

11

# Management command usage

12

python manage.py graph_transitions [appname[.model[.field]]] --output file.png --layout dot

13

```

14

15

**Command Arguments:**

16

- `appname[.model[.field]]`: Optional specification to limit visualization scope

17

- `appname`: Visualize all FSM fields in the app

18

- `appname.model`: Visualize all FSM fields in specific model

19

- `appname.model.field`: Visualize specific FSM field only

20

21

**Command Options:**

22

- `--output, -o`: Output file path. File extension determines format (png, jpg, svg, pdf, dot)

23

- `--layout, -l`: GraphViz layout engine (dot, neato, circo, fdp, sfdp, twopi, osage, patchwork)

24

25

### Basic Usage Examples

26

27

Generate visualization for entire project:

28

29

```bash

30

# Create dot file output to stdout

31

python manage.py graph_transitions

32

33

# Generate PNG image of all state machines

34

python manage.py graph_transitions --output transitions.png

35

36

# Generate SVG with different layout

37

python manage.py graph_transitions --output workflow.svg --layout neato

38

```

39

40

Generate visualization for specific app:

41

42

```bash

43

# All FSM fields in 'orders' app

44

python manage.py graph_transitions orders --output orders.png

45

46

# Specific model in app

47

python manage.py graph_transitions orders.Order --output order_states.png

48

49

# Specific field in model

50

python manage.py graph_transitions orders.Order.status --output order_status.png

51

```

52

53

### Graph Generation Functions

54

55

The command uses several internal functions that can be imported for programmatic use:

56

57

```python { .api }

58

def generate_dot(fields_data):

59

"""

60

Generate GraphViz representation of FSM transitions.

61

62

Parameters:

63

- fields_data: List of (field, model) tuples

64

65

Returns:

66

graphviz.Digraph: GraphViz graph object

67

"""

68

69

def all_fsm_fields_data(model):

70

"""

71

Extract FSM fields from model.

72

73

Parameters:

74

- model: Django model class

75

76

Returns:

77

List of (field, model) tuples for FSM fields

78

"""

79

80

def node_name(field, state):

81

"""

82

Generate unique node name for GraphViz.

83

84

Parameters:

85

- field: FSM field instance

86

- state: State value

87

88

Returns:

89

str: Unique node identifier

90

"""

91

92

def node_label(field, state):

93

"""

94

Generate human-readable node label.

95

96

Parameters:

97

- field: FSM field instance

98

- state: State value

99

100

Returns:

101

str: Display label for node

102

"""

103

```

104

105

## Visualization Features

106

107

### State Representation

108

109

States are represented as nodes with different visual styles:

110

111

- **Regular states**: Circular nodes

112

- **Final states**: Double-circle nodes (states with no outgoing transitions)

113

- **Initial states**: Connected to a small point node indicating entry

114

115

### Transition Representation

116

117

Transitions are represented as directed edges between state nodes:

118

119

- **Edge labels**: Show transition method names

120

- **Dotted edges**: Represent error transitions (on_error paths)

121

- **Multiple sources**: Transitions from multiple states are properly handled

122

123

### Wildcard Transitions

124

125

Special transition types are visualized appropriately:

126

127

- **Source="*"**: Transitions from any state show connections from all existing states

128

- **Source="+"**: Transitions from any state except target show appropriate connections

129

130

### Layout Options

131

132

Different GraphViz layout engines provide various visualization styles:

133

134

- **dot**: Hierarchical layout (default) - good for workflow-like state machines

135

- **neato**: Spring model layout - good for undirected-like graphs

136

- **circo**: Circular layout - good for cyclic state machines

137

- **fdp**: Force-directed layout - good for large graphs

138

- **sfdp**: Scalable force-directed layout - for very large graphs

139

- **twopi**: Radial layout - good for tree-like structures

140

- **osage**: Cluster layout - good for clustered graphs

141

- **patchwork**: Squarified treemap layout

142

143

## Programmatic Usage

144

145

### Generate Graphs in Code

146

147

Use the graph generation functions programmatically:

148

149

```python

150

from django_fsm.management.commands.graph_transitions import generate_dot, all_fsm_fields_data

151

from myapp.models import Order, Document

152

153

# Generate graph for specific models

154

fields_data = []

155

for model in [Order, Document]:

156

fields_data.extend(all_fsm_fields_data(model))

157

158

graph = generate_dot(fields_data)

159

160

# Save as different formats

161

graph.format = 'png'

162

graph.render('my_state_machines')

163

164

# Or get dot source

165

dot_source = str(graph)

166

print(dot_source)

167

```

168

169

### Integration with Documentation

170

171

Generate graphs as part of documentation builds:

172

173

```python

174

# In your documentation build script

175

import os

176

from django.core.management import call_command

177

178

def generate_fsm_docs():

179

"""Generate FSM visualizations for documentation."""

180

181

# Generate overview of all state machines

182

call_command('graph_transitions',

183

output='docs/images/all_fsm.png',

184

layout='dot')

185

186

# Generate specific model visualizations

187

models_to_document = [

188

'orders.Order.status',

189

'documents.Document.state',

190

'workflows.Task.workflow_state'

191

]

192

193

for model_spec in models_to_document:

194

output_name = model_spec.replace('.', '_').lower()

195

call_command('graph_transitions', model_spec,

196

output=f'docs/images/{output_name}.png',

197

layout='dot')

198

```

199

200

### Custom Graph Styling

201

202

Customize graph appearance by modifying the generate_dot function:

203

204

```python

205

import graphviz

206

from django_fsm.management.commands.graph_transitions import generate_dot as base_generate_dot

207

208

def styled_generate_dot(fields_data, style_config=None):

209

"""Generate styled FSM graph."""

210

graph = base_generate_dot(fields_data)

211

212

if style_config:

213

# Apply custom styling

214

graph.graph_attr.update(style_config.get('graph', {}))

215

graph.node_attr.update(style_config.get('node', {}))

216

graph.edge_attr.update(style_config.get('edge', {}))

217

218

return graph

219

220

# Usage with custom styling

221

style = {

222

'graph': {'bgcolor': 'white', 'dpi': '300'},

223

'node': {'style': 'filled', 'fillcolor': 'lightblue'},

224

'edge': {'color': 'darkblue', 'fontsize': '10'}

225

}

226

227

fields_data = all_fsm_fields_data(Order)

228

styled_graph = styled_generate_dot(fields_data, style)

229

styled_graph.render('styled_order_fsm')

230

```

231

232

## Advanced Visualization Patterns

233

234

### Multi-Field Visualization

235

236

Visualize models with multiple FSM fields:

237

238

```python

239

class Order(models.Model):

240

payment_status = FSMField(default='unpaid')

241

fulfillment_status = FSMField(default='pending')

242

243

# Payment transitions

244

@transition(field=payment_status, source='unpaid', target='paid')

245

def process_payment(self):

246

pass

247

248

# Fulfillment transitions

249

@transition(field=fulfillment_status, source='pending', target='shipped')

250

def ship_order(self):

251

pass

252

253

# The graph_transitions command will show both state machines

254

# as separate subgraphs within the same visualization

255

```

256

257

### State Machine Documentation

258

259

Generate documentation with embedded graphs:

260

261

```python

262

def generate_fsm_documentation(model_class):

263

"""Generate markdown documentation with embedded graphs."""

264

from django_fsm.management.commands.graph_transitions import all_fsm_fields_data, generate_dot

265

266

fields_data = all_fsm_fields_data(model_class)

267

268

documentation = f"""

269

# {model_class.__name__} State Machine

270

271

## State Diagram

272

273

![State Diagram](./{model_class.__name__.lower()}_fsm.png)

274

275

## States and Transitions

276

277

"""

278

279

# Generate the graph

280

graph = generate_dot(fields_data)

281

graph.format = 'png'

282

graph.render(f'{model_class.__name__.lower()}_fsm')

283

284

# Add transition documentation

285

for field, model in fields_data:

286

documentation += f"\n### {field.name.title()} Field\n\n"

287

288

transitions = field.get_all_transitions(model)

289

for transition in transitions:

290

documentation += f"- **{transition.name}**: {transition.source} → {transition.target}\n"

291

292

return documentation

293

```

294

295

### Interactive Visualization

296

297

Create interactive HTML visualizations:

298

299

```python

300

def generate_interactive_fsm(model_class, output_file):

301

"""Generate interactive HTML FSM visualization."""

302

from django_fsm.management.commands.graph_transitions import all_fsm_fields_data, generate_dot

303

304

fields_data = all_fsm_fields_data(model_class)

305

graph = generate_dot(fields_data)

306

307

# Generate SVG for interactivity

308

graph.format = 'svg'

309

svg_content = graph.pipe(format='svg').decode('utf-8')

310

311

# Wrap in HTML with interactivity

312

html_template = f"""

313

<!DOCTYPE html>

314

<html>

315

<head>

316

<title>{model_class.__name__} State Machine</title>

317

<style>

318

.transition {{ cursor: pointer; }}

319

.transition:hover {{ stroke-width: 3; }}

320

.state {{ cursor: pointer; }}

321

.state:hover {{ fill: yellow; }}

322

</style>

323

</head>

324

<body>

325

<h1>{model_class.__name__} State Machine</h1>

326

{svg_content}

327

<script>

328

// Add click handlers for interactivity

329

document.querySelectorAll('.transition').forEach(edge => {{

330

edge.addEventListener('click', function() {{

331

alert('Transition: ' + this.textContent);

332

}});

333

}});

334

</script>

335

</body>

336

</html>

337

"""

338

339

with open(output_file, 'w') as f:

340

f.write(html_template)

341

```

342

343

### Automated Graph Updates

344

345

Set up automated graph regeneration:

346

347

```python

348

# In your Django settings or management command

349

from django_fsm.signals import post_transition

350

from django.dispatch import receiver

351

352

@receiver(post_transition)

353

def update_fsm_documentation(sender, **kwargs):

354

"""Regenerate FSM graphs when models change."""

355

if settings.DEBUG: # Only in development

356

from django.core.management import call_command

357

358

try:

359

call_command('graph_transitions',

360

f'{sender._meta.app_label}.{sender.__name__}',

361

output=f'docs/graphs/{sender.__name__.lower()}_fsm.png')

362

except Exception as e:

363

logger.warning(f"Failed to update FSM graph for {sender}: {e}")

364

```

365

366

## Troubleshooting Visualizations

367

368

### Common Issues

369

370

**GraphViz not installed:**

371

```bash

372

# Install GraphViz system dependency

373

# Ubuntu/Debian:

374

sudo apt-get install graphviz

375

376

# macOS:

377

brew install graphviz

378

379

# Windows: Download from graphviz.org

380

```

381

382

**Missing Python graphviz package:**

383

```bash

384

pip install graphviz

385

```

386

387

**Large graphs are unreadable:**

388

- Use `--layout sfdp` for large graphs

389

- Generate specific model/field visualizations instead of entire project

390

- Increase DPI in programmatic usage

391

392

**Complex transitions are cluttered:**

393

- Use hierarchical layout (`--layout dot`)

394

- Split complex models into multiple visualizations

395

- Consider simplifying transition logic if visualization is too complex