tree traversal in template or view?

From what I understand, business logic should be confined to views, with templates handling display. Is what is defined as ‘display logic’ always clear-cut though, or is there some grey area here as to if and when it’s appropriate for your templates to do a little more ‘heavy-lifting’ so to speak?
For example, in my case in one of my views.py functions I constructed a tree as a set of nodes classes:

class Node:
    def __init__(self, data):
        self.data = data
        self.children = []

then set about querying my database for appropriate model objects to populate my nodes (setting the nodes data = <the-model-object>). I need my template to process the tree in pre-order for the purpose of generating a drop-down tree menu to appear.

My question is: Should I be passing the root node as a variable to the template, and then getting the template to traverse the tree in pre-order, requiring a stack (not even sure how to do this in DTL sytax) or should I do this in the view and create a more “template-friendly” custom data structure for the template to iterative over? I’m guessing the latter is the better option but in that case the only thing I can think of would probably be an arcane looking list of lists. eg for this tree


it might look something like [root[a[d, e], b[f], c[g[h, i]]]]. I guess the template would have a much easier time processing this but in that case there’ll have been no point me building the tree using a Node class in the first place, I might as well go straight from database model objects to this list of lists… But intuitively it feels like bad practise, because it will be confusing for another programmer to read my code.

<opinion>
It’s never appropriate to expect the template to perform any of the heavy lifting. In the case of a menu structure, what you really want to do is store your menu tree in a format that facilitates a pre-order transversal (e.g. something like a materialized path tree structure).

What we’ve done is use an MP tree to define the menu structure, with permissions assigned at the lowest level. The menu for an individual is loaded when they log in - intermediate levels only exist if they have access to one of the lowest-level entries. It’s a simple query that yields a JSON object, which is then fed directly to our JavaScript-based menu widget.

Another option is to find an existing project that manages a data-driven menu structure and build your data structures to satisfy that tool.
</opinion>

Ken

Thanks Ken, I will research materialized path tree stuctures. In your second paragraph, who do you mean when you say “we”? Is that a quote from a specific projects docs or your suggestion of how I should implement it?

Mike

The “we” refers to the company kind enough to keep me employed for the past almost-five years. We have an internal project where different people see slightly different menus. (About 75% same, 25% that changes by role.)

FYI, the implementation we use is django-treebeard

Ken

Thank you so much for pointing me in this direction - treebeard is an amazing library. I’ll have to reorganise the models in my project to use it since I was using a more standard organisation of nodes having parent pointers, list of children etc, but I reckon it’s probably worth it. Although not sure I fully understand the caveats mentioned in the treebeard docs so maybe I’m wrong.

Thanks,
Michael

Yes it is. In practice, we haven’t had any problems with it, nor have found the caveats to create any real limitations.
Admittedly, an MP tree is more effective for trees that are rarely updated relative to the frequency of retrieval. (If you’ve got a more dynamic tree structure, one of the other options are probably better.)

In my case it won’t be very dynamic tree, retrieval will be much more frequent than add new nodes, and I don’t want nodes to be deleted, so will definitely use MP.

I was wondering, with the standard tree approach, I was using 2 tables, one to hold all the root nodes and one to hold all descendents of the root nodes. It made sense for two reasons:
(1) Since root node could have lots of attributes the descendents wouldn’t need to have (they could just have a pointer back to the root node to access these when needed)
(2) Easy to display all the root nodes as anchors in html

I was wondering if you’d advise me to still keep this strategy when redesigning using treebeard or switch to just one single table? Getting all root nodes for display is easy with get_root_nodes() but like I said, it seems bad DB design for each of the nodes to have loads of model fields when I only need the root one to have it and for them to call get_root() to get that data.

Thanks
Mike

For the situation you describe - root nodes with additional model fields - I’d go with a One-to-one relationship with a “details” table. I’d keep the tree structure intact, regardless of how I implement the tree in the tables. Any additional data would be in related tables associated with the entries.

(For example, in the case I referenced above, the bottom level entries are restricted by role. Those role permissions are in a separate table, associated to the tree table by a 1-to-1 field.)

Using this still makes it easy to retrieve all the root nodes - it would just be the set of nodes in that root-related table. Likewise, if you’re using the MP tree structure within treebeard, you can retrieve those root-associated attributes from your leaf nodes by searching for the entries in that root-related table with the same first step prefix.

We use treebeard in another situation where the top two levels each have separate level-related data and levels 3 and below have a third. So we have three tables, each with a 1-to-1 relationship to the tree-table, each with their own set of attributes. We keep the tree-table as small as possible - it’s purpose is only to define the hierarchical relationships between components. The data about those components is stored elsewhere.

Ken

1 Like