Flask中的动态导航

7 投票
2 回答
11433 浏览
提问于 2025-04-17 19:38

我有一个简单的网站,使用Flask框架,数据存储在sqlite数据库里。每个页面的信息都存储在一个叫做“页面表”的地方,这个表里有路径、标题和内容等信息。

这个结构是层级式的,也就是说一个页面可以有一个父页面。比如说,“关于”这个页面可以有子页面,比如“关于/某个东西”和“关于/蛋糕”。我想创建一个导航栏,里面有所有以“/”作为父页面的链接(“/”是根页面)。另外,我还希望能显示当前打开的页面以及这个页面的所有父页面。

举个例子,如果我们在“关于/蛋糕/松饼”这个页面,除了总是显示的链接外,我们还希望看到指向“关于/蛋糕”的链接,像这样:

- 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)

我感觉自己在违反“不要重复自己”的原则,因为这里有两个函数。但如果要做导航的话,这个原则会被进一步违反,因为我还想在错误页面等地方也显示导航。

不过,我似乎找不到特别适合Flask的做法。有没有什么好主意?

2 个回答

2

你可以通过使用多个装饰器,在一个函数里完成这个操作 :)

@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

9

用“flasky”和符合python风格的方法,就是使用基于类的视图和模板层级。

首先,先看看这两个的文档,然后你可以根据这个方法来重构你的代码:

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"

然后你的模板: main_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。保持简单!
注意第一行。你的视图会设置应该显示哪个页面,你可以通过设置view-class的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 %}

撰写回答