【问题标题】:Generating a recursive sitemap with relative href links使用相对 href 链接生成递归站点地图
【发布时间】:2017-10-31 11:27:16
【问题描述】:

我正在使用 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>

文件夹结构示例:

显示file1.html 内容的整体外观:

【问题讨论】:

  • 您是否尝试过构建包含所有路径名的树,如下所示:stackoverflow.com/questions/10961378/…
  • @jbndlr 这正是我最初看到的帖子。这就是我结束的地方。幸运的是,我找到了解决自己遇到的问题的方法。谢谢。

标签: python jinja2


【解决方案1】:

使用烧瓶,我使用以下内容构建了站点地图

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 路由,它是动态的。

【讨论】:

  • 这很好,但是,正如您所怀疑的,这不是我要找的。不过还是谢谢分享!
【解决方案2】:

所以我想出了一个令人满意的方法来解决我自己的问题。

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

请注意,我的模板仅适用于扩展名为 .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后,我使用app.static_folder设置DOCS_ROOT
  • 在函数send_docs()中,我将send_from_directory()directory参数改为使用DOCS_ROOT
  • make_tree() 内部,在for 循环的else 块内,我添加了:

    np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
    if np.startswith('/'):
        np = np[1:]
    

    所有这一切都是取name的绝对路径,删除匹配DOCS_ROOT的,只留下相对路径(然后将\\替换为/),从而得到来自@的简单相对路径987654345@。如果相对路径以/ 开头,我将其删除(因为在template.html 中有来自docs 的尾随/


对于任何对我使用的简单样式表 (static\styles.css) 感兴趣的人(以及一些更新的增强功能):

html {
    min-height:100%;
    position:relative;
}

body {
    overflow:hidden;
}

.container {
    width:100%;
    overflow:auto;
}

.left_frame {
    float:left;
    background:#E8F1F5;
    width:25%;
    height:100vh;
}

.right_frame {
    float:right;
    background:#FAFAFA;
    width:75%;
    height:100vh;
}

.right_frame iframe {
    display:block;
    width:100%;
    height:100%;
    border:none;
}

【讨论】:

    【解决方案3】:

    添加到@HEADLESS_0NE 的解决方案中:

    他在for loop 中又添加了几条if 语句,如下所示:

           {%- 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 %}
    

    -&gt; 中的所有内容都在html 中进行了更改,我找不到他在pythoncss 中添加的内容,但总而言之:

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

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

    对于更详细的解释,我希望 HEADLESS_0NE 可以向我们提供更多信息,他可能在 python 脚本中进行了哪些更改。

    【讨论】:

    • 感谢您花时间进一步解释我对 Jinja 模板文件所做的更改。我的示例文件没有版本控制,所以我会尽力而为。
    • @HEADLESS_0NE 没问题 :) 我尽力解释得更详细!感谢您更新您的帖子并说明您如何解决它;)
    • @HEADLESS_0NE 感谢您更详细地解释python :) 我很感激 ^^ 已经在问答中给出了 +1 :) 希望它可以帮助很多有同样问题的用户!
    猜你喜欢
    • 2010-09-24
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 2015-01-31
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    相关资源
    最近更新 更多