【问题标题】:Dynamic navigation in FlaskFlask 中的动态导航
【发布时间】:2013-03-08 05:55:50
【问题描述】:

我有一个在 Flask 中工作的非常简单的站点,它全部由 sqlite 数据库提供支持。每个页面在页表中存储为一行,其中包含路径、标题、内容等内容。

结构是分层的,页面可以有父级。因此,例如,虽然“关于”可能是一个页面,但也可能有“关于/某事”和“关于/蛋糕”。所以我想创建一个导航栏,其中包含指向所有具有“/”父级的链接的链接(/ 是根页面)。此外,我希望它还显示打开的页面以及该页面的所有父页面。

因此,例如,如果我们在“about/cakes/muffins”,除了始终显示的链接外,我们还会以某种方式看到“about/cakes”的链接:

- About/
  - Cakes/
    - Muffins
    - Genoise
  - Pies/
- Stuff/
- Contact
- Legal
- Etc.[/]

带有子页面的页面带有斜杠,没有子页面的页面没有。

代码:

@app.route('/')
def index():
    page = query_db('select * from page where path = "/"', one=True)
    return render_template('page.html', page=page, bread=[''])

@app.route('/<path>')
def page(path=None):
    page = query_db('select * from page where path = "%s"' % path, one=True)
    bread = Bread(path)
    return render_template('page.html', page=page, crumbs=bread.links)

我已经觉得我违反了 DRY 有两个功能。但是进行导航会进一步违反它,因为我还希望在错误页面等内容上进行导航。

但我似乎找不到一种特别 Flasky 的方法来做到这一点。有什么想法吗?

【问题讨论】:

  • 对不起,我不完全确定你在这里问什么 - 究竟该怎么做?您在“页面”视图中构建 SQL 查询的方式是不安全的,并且容易受到注入攻击。查看here 的一些答案,了解如何安全处理用户输入。
  • 是的,都是临时代码,我知道SQL注入。我在问如何在 Flask 中获得动态树导航。我以前在 django 中使用 mppt 做过这个,但我在任何地方都没有看到 Flask 等价物。
  • 我有一个类似的问题,但还需要导航才能访问 flask.session 变量。见stackoverflow.com/questions/45948609/…

标签: python flask


【解决方案1】:

你可以通过多个装饰器在一个函数中完成它:)

@app.route('/', defaults={'path': '/'})
@app.route('/<path>')
def page(path):
    page = query_db('select * from page where path = "%s"' % path, one=True)
    if path == '/':
        bread = Bread(path)
        crumbs = bread.links
    else:
        bread = ['']
        crumbs = None
    return render_template('page.html', page=page, bread=bread, crumbs=crumbs)

我个人会修改面包函数,使其也适用于路径/

如果只是将变量添加到您的上下文中,那么我建议您查看上下文处理器:http://flask.pocoo.org/docs/templating/#context-processors

【讨论】:

  • 我感兴趣的不是面包屑,而是那些工作。我正在完成剩下的导航工作。
  • @Knyght:你看到关于上下文处理器的部分了吗?那些应该能够为您很好地解决这个问题。只需编写一个小的上下文处理器来根据给定的路径(或请求)自动生成导航菜单,它应该可以工作。
  • 另外,哎呀,我前段时间改变了 bread() 的工作方式,忘记更新第一个函数,这是你重复自己时得到的,derp。感谢多个装饰器的提示。我将研究上下文处理器。谢谢。
【解决方案2】:

“flasky”和 pythonic 方式将是使用基于类的视图和模板层次结构

首先阅读两者的文档,然后您可以根据这种方法重构您的代码:

class MainPage(MethodView):
    navigation=False
    context={}

    def prepare(self,*args,**kwargs):
        if self.navigation:
            self.context['navigation']={
                #building navigation
                #in your case based on request.args.get('page')
            }
        else:
            self.context['navigation']=None

    def dispatch_request(self, *args, **kwargs):
        self.context=dict() #should nullify context on request, since Views classes objects are shared between requests
        self.prepare(self,*args,**kwargs)
        return super(MainPage,self).dispatch_request(*args,**kwargs)

class PageWithNavigation(MainPage):
    navigation = True

class ContentPage(PageWithNavigation):
    def get(self):
        page={} #here you do your magic to get page data
        self.context['page']=page
        #self.context['bread']=bread
        #self.context['something_Else']=something_Else
        return render_template('page.html',**self.context)

然后您可以执行以下操作: 为 main_page.html 和 page_with_navigation.html 创建单独的页面 然后你的每一页“error.html,page.html,somethingelse.html”都基于其中一个。 关键是动态地做到这一点:

将修改prepare方法:

def prepare(self):
        if self.navigation:
            self.context['navigation']={
                #building navigation
                #in your case based on request.args.get('page')
            }
        else:
            self.context['navigation']=None
        #added another if to point on changes, but you can combine with previous one
        if self.navigation:
            self.context['extends_with']="templates/page_with_navigation.html"
        else:
            self.context['extends_with']="templates/main_page.html"

还有你的模板: ma​​in_page.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    {% block navigation %}
    {% endblock %}
    {% block main_content %}
    {% endblock %}
</body>
</html>

page_with_navigation.html

{% extends "/templates/main_page.html" %}

{% block navigation %}
        here you build your navigation based on navigation context variable, which already passed in here
{% endblock %}

page.html 或任何其他 some_page.html。保持简单!
注意第一行。您的视图设置应该进入哪个页面,您可以通过设置视图类的 navigation= 轻松调整它。

{% extends extends_with %}

{% block main_content %}
        So this is your end-game page.
        Yo do not worry here about navigation, all this things must be set in view class and template should not worry about them
        But in case you need them they still available in navigation context variable
{% endblock %}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    相关资源
    最近更新 更多