I have a pretty simple site working in Flask that's all powered from an sqlite db. Each page is stored as a row in the page table, which holds stuff like the path, title, content.
The structure is hierarchical where a page can have a parent. So while for example, 'about' may be a page, there could also be 'about/something' and 'about/cakes'. So I want to create a navigation bar with links to all links that have a parent of '/' (/ is the root page). In addition, I'd like it to also show the page that is open and all parents of that page.
So for example if we were at 'about/cakes/muffins', in addition to the links that always show, we'd also see the link to 'about/cakes', in some manner like so:
- About/
- Cakes/
- Muffins
- Genoise
- Pies/
- Stuff/
- Contact
- Legal
- Etc.[/]
with trailing slashes for those pages with children, and without for those that don't.
Code:
@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)
I already feel like I'm violating DRY for having two functions there. But doing navigation will violate it further, since I also want the navigation on things like error pages.
But I can't seem to find a particularly Flasky way to do this. Any ideas?
In this lesson, you will learn how to add more pages to your flask website. We will quickly add an About page. Second, you need to render the HTML with Python by adding a second function to the hello.py Python script. Run the Python script and go to localhost:5000/about, and you will see the new page.
The "flasky" and pythonic way will be to use class-based view and templates hierarchy
First of all read documentation on both, then you can refactor your code based on this approach:
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)
Then you can do following: create separate pages, for main_page.html and page_with_navigation.html Then your every page "error.html, page.html, somethingelse.html" based on one of them. The key is to do this dynamically:
Will modify prepare method a bit:
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"
And your templates: 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 or any other some_page.html. Keep it simple!
Pay attention to first line. Your view sets up which page should go in there and you can easily adjust it by setting navigation= of view-class.
{% 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 %}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With