Skip to content

Commit 4bd42ac

Browse files
authored
Add 'ignore' to graph_transition command (#56)
1 parent 04be14b commit 4bd42ac

File tree

3 files changed

+43
-19
lines changed

3 files changed

+43
-19
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ $ ./manage.py graph_transitions > transitions.dot
429429

430430
# Create a PNG image file only for specific model
431431
$ ./manage.py graph_transitions -o blog_transitions.png myapp.Blog
432+
433+
# Exclude some transitions
434+
$ ./manage.py graph_transitions -e transition_1,transition_2 myapp.Blog
432435
```
433436

434437
## Extensions

django_fsm/management/commands/graph_transitions.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,31 @@ def all_fsm_fields_data(model):
1616
return [(field, model) for field in model._meta.get_fields() if isinstance(field, FSMFieldMixin)]
1717

1818

19-
def node_name(field, state):
19+
def node_name(field, state) -> str:
2020
opts = field.model._meta
2121
return "{}.{}.{}.{}".format(opts.app_label, opts.verbose_name.replace(" ", "_"), field.name, state)
2222

2323

24-
def node_label(field, state):
25-
if isinstance(state, int) or (isinstance(state, bool) and hasattr(field, "choices")):
24+
def node_label(field, state) -> str:
25+
if isinstance(state, int):
26+
return str(state)
27+
if isinstance(state, bool) and hasattr(field, "choices"):
2628
return force_str(dict(field.choices).get(state))
2729
return state
2830

2931

30-
def generate_dot(fields_data): # noqa: C901, PLR0912
32+
def generate_dot(fields_data, ignore_transitions: list[str] | None = None): # noqa: C901, PLR0912
33+
ignore_transitions = ignore_transitions or []
3134
result = graphviz.Digraph()
3235

3336
for field, model in fields_data:
3437
sources, targets, edges, any_targets, any_except_targets = set(), set(), set(), set(), set()
3538

3639
# dump nodes and edges
3740
for transition in field.get_all_transitions(model):
41+
if transition.name in ignore_transitions:
42+
continue
43+
3844
_targets = list(
3945
(state for state in transition.target.allowed_states)
4046
if isinstance(transition.target, (GET_STATE, RETURN_VALUE))
@@ -127,7 +133,7 @@ def add_arguments(self, parser):
127133
"-o",
128134
action="store",
129135
dest="outputfile",
130-
help=("Render output file. Type of output dependent on file extensions. Use png or jpg to render graph to image."),
136+
help="Render output file. Type of output dependent on file extensions. Use png or jpg to render graph to image.",
131137
)
132138
parser.add_argument(
133139
"--layout",
@@ -137,6 +143,14 @@ def add_arguments(self, parser):
137143
default="dot",
138144
help=f"Layout to be used by GraphViz for visualization. Layouts: {get_graphviz_layouts()}.",
139145
)
146+
parser.add_argument(
147+
"--exclude",
148+
"-e",
149+
action="store",
150+
dest="exclude",
151+
default="",
152+
help="Ignore transitions with this name.",
153+
)
140154
parser.add_argument("args", nargs="*", help=("[appname[.model[.field]]]"))
141155

142156
def render_output(self, graph, **options):
@@ -153,9 +167,8 @@ def handle(self, *args, **options):
153167
field_spec = arg.split(".")
154168

155169
if len(field_spec) == 1:
156-
app = apps.get_app(field_spec[0])
157-
models = apps.get_models(app)
158-
for model in models:
170+
app = apps.get_app_config(field_spec[0])
171+
for model in apps.get_models(app):
159172
fields_data += all_fsm_fields_data(model)
160173
if len(field_spec) == 2: # noqa: PLR2004
161174
model = apps.get_model(field_spec[0], field_spec[1])
@@ -166,7 +179,8 @@ def handle(self, *args, **options):
166179
else:
167180
for model in apps.get_models():
168181
fields_data += all_fsm_fields_data(model)
169-
dotdata = generate_dot(fields_data)
182+
183+
dotdata = generate_dot(fields_data, ignore_transitions=options["exclude"].split(","))
170184

171185
if options["outputfile"]:
172186
self.render_output(dotdata, **options)

tests/testapp/tests/test_graph_transitions.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@
77

88

99
class GraphTransitionsCommandTest(TestCase):
10-
def test_dummy(self):
11-
call_command("graph_transitions", "testapp.Application")
10+
MODELS_TO_TEST = [
11+
"testapp.Application",
12+
"testapp.FKApplication",
13+
]
1214

13-
def test_layouts(self):
14-
for layout in get_graphviz_layouts():
15-
call_command("graph_transitions", "-l", layout, "testapp.Application")
15+
def test_app(self):
16+
call_command("graph_transitions", "testapp")
1617

17-
def test_fk_dummy(self):
18-
call_command("graph_transitions", "testapp.FKApplication")
18+
def test_single_model(self):
19+
for model in self.MODELS_TO_TEST:
20+
call_command("graph_transitions", model)
1921

20-
def test_fk_layouts(self):
21-
for layout in get_graphviz_layouts():
22-
call_command("graph_transitions", "-l", layout, "testapp.FKApplication")
22+
def test_single_model_with_layouts(self):
23+
for model in self.MODELS_TO_TEST:
24+
for layout in get_graphviz_layouts():
25+
call_command("graph_transitions", "-l", layout, model)
26+
27+
def test_exclude(self):
28+
for model in self.MODELS_TO_TEST:
29+
call_command("graph_transitions", "-e", "standard,no_target", model)

0 commit comments

Comments
 (0)