使用相对href链接生成递归站点地图

时间:2017-05-30 20:40:20

标签: python jinja2

我正在使用Flask在网页上公开HTML文件的本地目录。

我还使用jinja2在我的主要终端的左侧div生成站点地图。

无法正确指定子文件夹端点的URL。

如下面的代码所述,我如何动态构建/docs的相对链接(即/docs/folder1/subfolder1/SubFolder1Page.html)? 我目前设置href值的方式显然不起作用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Docs Demo</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
    <div id="container">
        <div class="left_frame">
            <h1>{{ tree.name }}</h1>
            <ul>
            {%- for item in tree.children recursive %}
                <!-- How would I build a relative link from /docs/ i.e. /docs/folder1/subfolder1/SubFolder1Page.html -->
                <li><a href="docs/{{ item.name }}" target="iframe1">{{ item.name }}
                {%- if item.children -%}
                    <ul>{{ loop(item.children) }}</ul>
                {%- endif %}</a></li>
            {%- endfor %}
            </ul>
        </div>
        <div class="right_frame">
            <iframe name="iframe1"></iframe>
        </div>
    </div>
</body>
</html>

文件夹结构示例:

demo structure

整体显示file1.html的内容的方式如何: sitemap view

3 个答案:

答案 0 :(得分:5)

所以我找到了解决自己问题的令人满意的方法。

我设法得到了这个非常实用的结果: Flask serving Docs demo

请注意,我的模板仅适用于扩展名为.html的文件,但可以轻松增强以支持其他文件扩展名。

这是我最终确定的templates\template.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Docs Demo</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
    <div id="container">
        <div class="left_frame">
            <h1>{{ tree.name }}</h1>
            <ul>
            {%- for item in tree.children recursive %}
                {% if '.html' in item.name %}
                    <li><a href="docs/{{ item.name }}" target="iframe1">
                    {{ item.name.split('/')[-1:][0] }}
                    {%- if item.children -%}
                        <ul>{{ loop(item.children) }}</ul>
                    {%- endif %}</a></li>
                {% else %}
                    <li>{{ item.name }}
                    {%- if item.children -%}
                        <ul>{{ loop(item.children) }}</ul>
                    {%- endif %}</li>
                {% endif %}
            {%- endfor %}
            </ul>
        </div>
        <div class="right_frame">
            <iframe name="iframe1"></iframe>
        </div>
    </div>
</body>
</html>

您可以参考 King Reload 的回答,分析我在template.html文件中所做的更改,以使其正常工作。

这是demo_app.py脚本,通过Flask提供我的文档HTML文件:

import threading
import os
import webbrowser
from flask import Flask, render_template, send_from_directory


app = Flask(__name__, static_folder='static')

ROOT = os.path.dirname(os.path.abspath(__file__))
DOCS_ROOT = os.path.join(app.static_folder, 'docs')


@app.route('/')
def docs_tree():
    return render_template('template.html', tree=make_tree(DOCS_ROOT))


@app.route('/docs/<path:filename>')
def send_docs(filename):
    return send_from_directory(directory=DOCS_ROOT, 'docs'), filename=filename)


def make_tree(path):
    tree = dict(name=os.path.basename(path), children=[])
    try:
        lst = os.listdir(path)
    except OSError:
        pass  # ignore errors
    else:
        for name in lst:
            fn = os.path.join(path, name)
            if os.path.isdir(fn):
                tree['children'].append(make_tree(fn))
            else:
                np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
                if np.startswith('/'):
                    np = np[1:]
                tree['children'].append(dict(name=np))
    return tree

if __name__ == '__main__':
    host = 'localhost'
    port = '8888'
    url = 'http://{h}:{p}'.format(h=host, p=port)
    threading.Timer(3, lambda: webbrowser.open(url)).start()
    app.run(host=host, port=port, debug=False)

自问我原来的问题后demo_app.py中最显着的变化如下:

  • 初始化app后,我使用DOCS_ROOT设置app.static_folder;
  • 在函数send_docs()中,我更改了send_from_directory()的{​​{1}}参数以使用directory;
  • DOCS_ROOT内部,在make_tree()循环的else块内,我添加了:

    for

    所有这一切都取np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/') if np.startswith('/'): np = np[1:] 的绝对路径,删除与name匹配的内容,只保留相对路径(然后替换DOCS_ROOT \\),导致/的简单相对路径。如果相对路径以static/docs开头,我会将其删除(因为//的结尾docs

对于我使用的简单样式表(template.html)感兴趣的人(以及一些更新的增强功能):

static\styles.css

答案 1 :(得分:3)

添加到@ HEADLESS_0NE的解决方案:

他在if中添加了一些for loop语句,如下所示:

       {%- for item in tree.children recursive %}
    ->        {% if '.html' in item.name %}
                <li><a href="docs/{{ item.name }}" target="iframe1">
    ->            {{ item.name.split('/')[-1:][0] }}
                {%- if item.children -%}
                    <ul>{{ loop(item.children) }}</ul>
                {%- endif %}</a></li>
    ->        {% else %}
    ->            <li>{{ item.name }}
    ->            {%- if item.children -%}
    ->                <ul>{{ loop(item.children) }}</ul>
    ->            {%- endif %}</li>
    ->        {% endif %}
        {%- endfor %}

->中已更改html的所有内容,我无法找到他在pythoncss中添加的内容,但简而言之:

  • if检查.html中是否有item.name
  • item.name上的拆分,因此/被移除。
  • 如果else中没有.html,则为item.name声明。

这基本上会以正确的格式添加ulli

有关更详细的解释,我希望HEADLESS_0NE能够向我们提供更多信息,了解他可能在python脚本中发生的变化。

答案 2 :(得分:2)

使用烧瓶,我使用以下

构建了站点地图
from flask import url_for

def get_flask_resources():
    verbs = ["POST","GET","PUT","DELETE"]

    resources = {}
    for rule in app.url_map.iter_rules():
        if has_no_empty_params(rule):
            resource = url_for(rule.endpoint, **(rule.defaults or {}))
            if resource not in resources:
                resources[resource] = {}
            for verb in verbs:
                if verb in rule.methods:
                    resources[resource][verb] = {
                        'function':rule.endpoint,
                        'docs':app.view_functions[rule.endpoint].__doc__
                    }
        else:
            resource = rule.rule
            if resource not in resources:
                resources[resource] = {}
            for verb in verbs:
                if verb in rule.methods:
                    resources[resource][verb] = {
                        'function':rule.endpoint,
                        'docs':app.view_functions[rule.endpoint].__doc__
                    }

    return resources

此函数返回这样的字典

{
  "/endpoint1": {
    "GET": {
      "docs": "", 
      "function": "endpoint1"
    }
  }, 
  "/endpoint2": {
    "GET": {
      "docs": "", 
      "function": "endpoint2"
    }
  }, 
  "/endpoint1/something": {
    "POST": {
      "docs": "", 
      "function": "endpoint1_something"
    }
  }, 
}

我有一个端点返回此数据,然后在前端格式化它。字典键是您希望在链接中使用的URI。

这确实假设您将为每个HTML文档设置一个烧瓶路径,但情况可能并非如此。

使用此功能的一个好处是,如果您添加更多HTML文档/ Flask路由,它将是动态的。