Recursive Django filter to output a list from user hierarchy I need to create an HTML unordered list (to display with jstree) to display the hierarchy of users, along with a list underneath the user's "subusers" of their comments. Each user could have many users underneath him, and each of those users could have the same, so I used a recursive filter to accomplish this. However, this has lead to code that feels very repetitive, besides small differences, and it seems to me that surely something could be reduced. I have already identified some repeated code (extracted to _output_comments below), but I would hope I could do more somehow.

Note before the code: User has a foreign key to itself, with subusers as the related_name, and Comment has a foreign key to User.

from django import template

register = template.Library()


@register.filter
def unordered_user_list(user):
    """
    Takes a user and returns an HTML unordered list (without surrounding &lt;ul&gt; tags) of 
    users and comments under it.
    """
    html = ""
    if len(user.subusers.all()) &gt; 0:
        html += "&lt;li id='user-" + str(user.id) + "'&gt;" + user.name
        if len(user.comment_set.all()) &gt; 0:
            html += "&lt;ul&gt;"
            for subuser in user.subusers.all():
                html += unordered_user_list(subuser)
            html += "&lt;li&gt;Comments&lt;ul&gt;"
            html += _output_comments(user.comment_set.all())
        else:
            for subuser in user.subusers.all():
                html += "&lt;ul&gt;" + unordered_user_list(subuser) + "&lt;/ul&gt;"
        html += "&lt;/li&gt;"
    elif len(user.comment_set.all()) &gt; 0:
        html += "&lt;li id='user-" + str(user.id) + "'&gt;" + user.name + "&lt;ul&gt;&lt;li&gt;Comments&lt;ul&gt;"
        html += _output_comments(user.comment_set.all())
    else:
        html += "&lt;li id='user-" + str(user.id) + "'&gt;" + user.name + "&lt;/li&gt;"
    return html

def _output_comments(comment_list):
    output = ""
    for comment in comment_list:
        output += "&lt;li id='comment-" + str(comment.id) + "'&gt;" + comment.name + "&lt;/li&gt;"
    output += "&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;"
    return output


Finally, I would appreciate any other pointers, since this is my first filter. It seems straightforward enough, but for all I know this could be a completely inappropriate use of a filter.
First of all, you can move the first &lt;li&gt; out of the first if condition. That allows you to totally get rid of the last else:

html = "&lt;li id='user-" + str(user.id) + "'&gt;" + user.name
if len(user.subusers.all()) &gt; 0:
    # ...
elif len(user.comment_set.all()) &gt; 0:
    html += "&lt;ul&gt;&lt;li&gt;Comments&lt;ul&gt;"
    # ...
    # don't add the last &lt;/li&gt; here
html += "&lt;/li&gt;" # add it there instead
return html


Then, _output_comments should handle the whole comment stuff, including the heading:

def _output_comments(comment_list):
    output = "&lt;li&gt;Comments&lt;ul&gt;"
    for comment in comment_list:
        output += "&lt;li id='comment-" + str(comment.id) + "'&gt;" + comment.name + "&lt;/li&gt;"
    output += "&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;"
    return output


I think that the way you add &lt;li&gt;&lt;/li&gt; and &lt;ul&gt;&lt;/ul&gt; tags does not reflect the structure of the page. You should improve this:

def _output_comments(comment_list):
    output = ("&lt;li&gt;" +
              "Comments" +
              "&lt;ul&gt;")

    for comment in comment_list:
        output += "&lt;li id='comment-" + str(comment.id) + "'&gt;" + comment.name + "&lt;/li&gt;"

    output += ("&lt;/ul&gt;" +
               "&lt;/li&gt;" +
               "&lt;/ul&gt;")
    return output


Overall, you should try to write Python code whose structures more or less reflects the structure of the HTML code you are writing. That will avoid tricky mistakes and tag mismatches. When I read your code as is, I have trouble knowing whether there are tags mismatches or not.