Recursive Django filter to output a list from user hierarchy
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.