r_metal r_metal - 13 days ago 8
Python Question

No template named index

I'm trying to get a simple blog running on web.py, it launches fine(python bin/blog.py) but when i enter website it says it cannot find template. I'm pretty sure the path is right, i did a simple todo list before that and it works fine. What's wrong?

here's my project folder look:
-blogpy
-bin
-blogwebpy
-docs
-templates
-tests

Here's the code for blog.py which is located in bin(templates are located in templates):

import web
import model

### Url mappings

urls = (
'/', 'Index',
'/view/(\d+)', 'View',
'/new', 'New',
'/delete/(\d+)', 'Delete',
'/edit/(\d+)', 'Edit',
)


### Templates
t_globals = {
'datestr': web.datestr
}
render = web.template.render('templates/', base='base', globals=t_globals)


class Index:

def GET(self):
""" Show page """
posts = model.get_posts()
return render.index(posts)


class View:

def GET(self, id):
""" View single post """
post = model.get_post(int(id))
return render.view(post)


class New:

form = web.form.Form(
web.form.Textbox('title', web.form.notnull,
size=30,
description="Post title:"),
web.form.Textarea('content', web.form.notnull,
rows=30, cols=80,
description="Post content:"),
web.form.Button('Post entry'),
)

def GET(self):
form = self.form()
return render.new(form)

def POST(self):
form = self.form()
if not form.validates():
return render.new(form)
model.new_post(form.d.title, form.d.content)
raise web.seeother('/')


class Delete:

def POST(self, id):
model.del_post(int(id))
raise web.seeother('/')


class Edit:

def GET(self, id):
post = model.get_post(int(id))
form = New.form()
form.fill(post)
return render.edit(post, form)


def POST(self, id):
form = New.form()
post = model.get_post(int(id))
if not form.validates():
return render.edit(post, form)
model.update_post(int(id), form.d.title, form.d.content)
raise web.seeother('/')


app = web.application(urls, globals())

if __name__ == '__main__':
app.run()

Answer

As specified, render is looking for templates directory relative to the current working directory, i.e, from where you're trying to execute the program.

A few different ways to fix it:

  1. Execute from blogpy directory:

    python bin/blog.py
    

    This will correctly find templates directory in blogpy/templates.

  2. Execute from bin directory, but have render look in ../templates. This would require a small change to your code where you specify the location of the templates subdir. (This is what I suggest for your example).

  3. Execute from bin directory, but move the templates directory to be under the bin directory. This may look odd, but if you're looking for a simple solution, there's nothing wrong with it.

  4. Use fully specified path names. Ultimately, this is what you should be doing, though it's overkill for your example. You'll save time in the future having a block like:

    === repositories.py ===
    import os
    _dir_name = os.path.dirname(__file__) or os.getcwd()
    templates = os.path.normpath(os.path.join(_dir_name, "../templates"))
    

    You'll be able to locate all your subdirectories (for static content, maybe encryption keys, etc.) reliably regardless how the code is invoked.