How to load a javascript or css file into a BottlePy template?

18,706

Solution 1

Well, first, you need your dev server to actually serve main.js, otherwise it won't be available for the browser.

It's customary to put all .js and .css files under the static directory in small web apps, so your layout should look like this:

  app.py
- static/
    main.js
- views/
    index.tpl

By no means this exact naming and layout is required, only often used.

Next, you should supply a handler for the static files:

from bottle import static_file

# ...

@route('/static/:path#.+#', name='static')
def static(path):
    return static_file(path, root='static')

This will actuall serve your files under static/ to the browser.

Now, to the last thing. You specified your JavaScript as:

<script type="text/javascript" src="js/main.js" charset="utf-8"></script>

That means the path to .js is relative to the current page. On you development server, the index page (/) will look for .js in /js/main.js, and another page (say, /post/12) will look for it in /post/12/js/main.js, and will sure fail.

Instead, you need to use the get_url function to properly reference static files. Your handler should look like this:

from Bottle import get_url

# ...

@route('/')
@view('index')
def index():
    return { 'get_url': get_url } 

And in index.tpl, .js should be referenced as:

<script type="text/javascript" src="{{ get_url('static', path='main.js') }}" charset="utf-8"></script>

get_url finds a handler with name='static', and calculates the proper path to it. For dev server, this will always be /static/. You can probably even hard-code it in the template, but I don't recommend it for two reasons:

  • You won't be able to mount your app anywhere but under root in production; i.e., when you upload it onto the porduction server, it can be placed under http://example.com/ (root), but not under http://example.com/myapp/.
  • If you change the /static/ dir location, you'll have to search it all over your templates and modify it in every single template.

Solution 2

Here is a working approach of adding static files like CSS/JS in Bottle web project. I am using Bootstrap and Python 3.6.

Project structure

project
│   app.py
│   bottle.py
│
├───static
│   └───asset
│       ├───css
│       │       bootstrap-theme.css
│       │       bootstrap-theme.css.map
│       │       bootstrap-theme.min.css
│       │       bootstrap-theme.min.css.map
│       │       bootstrap.css
│       │       bootstrap.css.map
│       │       bootstrap.min.css
│       │       bootstrap.min.css.map
│       │       custom.css
│       │
│       ├───fonts
│       │       glyphicons-halflings-regular.eot
│       │       glyphicons-halflings-regular.svg
│       │       glyphicons-halflings-regular.ttf
│       │       glyphicons-halflings-regular.woff
│       │       glyphicons-halflings-regular.woff2
│       │
│       └───js
│               bootstrap.js
│               bootstrap.min.js
│               jquery.min.js
│               npm.js
│
└───views
        index.tpl

app.py

from bottle import Bottle, run, \
     template, debug, static_file

import os, sys

dirname = os.path.dirname(sys.argv[0])

app = Bottle()
debug(True)

@app.route('/static/<filename:re:.*\.css>')
def send_css(filename):
    return static_file(filename, root=dirname+'/static/asset/css')

@app.route('/static/<filename:re:.*\.js>')
def send_js(filename):
    return static_file(filename, root=dirname+'/static/asset/js')

@app.route('/')
def index():
    data = {"developer_name":"Ahmedur Rahman Shovon",
            "developer_organization":"Datamate Web Solutions"}
    return template('index', data = data)

run(app, host='localhost', port = 8080)

index.tpl

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Bottle web project template">
    <meta name="author" content="datamate">
    <link rel="icon" href="/static/favicon.ico">        
    <title>Project</title>
    <link rel="stylesheet" type="text/css" href="/static/bootstrap.min.css">
    <script type="text/javascript" src="/static/jquery.min.js"></script>
    <script type="text/javascript" src="/static/bootstrap.min.js"></script> 
</head>
<body>
    <!-- Static navbar -->
    <nav class="navbar navbar-default navbar-static-top">
        <div class="container">
            <div class="row">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="#">Project</a>
                </div>
                <div id="navbar" class="navbar-collapse collapse">
                    <ul class="nav navbar-nav navbar-right">
                        <li><a href="../navbar/">Home</a></li>
                        <li><a href="./">Github</a></li>
                        <li><a href="../navbar-fixed-top/">Stackoverflow</a></li>
                    </ul>
                </div><!--/.nav-collapse -->
            </div>
        </div>
    </nav>
    <div class="container">
        <div class="row">
            <div class="jumbotron">
            <h2>Welcome from {{data["developer_name"]}}</h2>
                <p>This is a template showcasing the optional theme stylesheet included in Bootstrap. Use it as a starting point to create something more unique by building on or modifying it.</p>
            </div>
        </div>
        <!--./row-->
        <div class="row">
            <hr>
            <footer>
                <p>&copy; 2017 {{data["developer_organization"]}}.</p>
            </footer>           
        </div>
    </div> 
    <!-- /container -->
</body>
</html>

Output

bottle bootstrap demo

Share:
18,706
eltorrero
Author by

eltorrero

Updated on June 14, 2022

Comments

  • eltorrero
    eltorrero about 2 years

    I am trying to return a html template with BottlePy. And this works fine. But if I insert a javascript file like this in my tpl-file:

    <script type="text/javascript" src="js/main.js" charset="utf-8"></script>
    

    I get an 404 error. (Failed to load resource: the server responded with a status of 404 (Not Found))

    Does anyone know how to fix this problem?

    Here is my script file:

    from bottle import route, run, view
    
    @route('/')
    @view('index')
    def index():
        return dict()
    run(host='localhost', port=8080)
    

    And that is the template file, located in "./views" subfolder.

    <!DOCTYPE html>
    <html lang="de">
        <head>
            <meta charset="utf-8" />
            <script type="text/javascript" src="js/main.js" charset="utf-8"></script>
        </head>
        <body>
        </body>
    </html>
    

    Maybe it is the "rootPath/js/main.js" from the development server where it looks for my js-file?

    The structure of the files is:

    app.py
    -js
     main.js
    -views
     index.tpl
    

    Thanks.

    • Samuele Mattiuzzo
      Samuele Mattiuzzo almost 13 years
      have you tried some sort of debugging, like printing out your root path and your js folder content?
  • Anuj
    Anuj over 12 years
  • Helgi
    Helgi over 12 years
    @Anuj: I've answered your question.
  • Lucio
    Lucio almost 10 years
    @Helgi What does the #.+# expression at the route do?
  • Admin
    Admin about 9 years
    @Lucio, the expression between the # is a regex (regular expression). The . character matches any character and + means any number but at least one. Therefore, the regex is matching any string that has a length of at least one character.
  • Teddy Katayama
    Teddy Katayama over 3 years
    Thank you for a simple example. Depending on how you run app.py, using os.path.dirname(sys.argv[0]) may not get the correct path. For example, I am running a Bottle server as a daemon service where I set a WorkingDirectory that points to where app.py exists. Whenever I need the location for any python script I use this hack sys.path[0] as python will typically load the script's path into sys.path prior to launching. So to get your solution to work, I use:
  • Teddy Katayama
    Teddy Katayama over 3 years
    dirname = sys.path[0] + '/'