Django webframework relative path to .css and other .html files

5.1k views Asked by At

This is what my project directory briefly looks like (not complete picture). I am having trouble linking many components together (.css, .html).

├── bootstrap-3.3.5-dist
└── mysite
    ├── db.sqlite3
    ├── myApp
    │   ├── admin.py
    │   ├── forms.py
    │   ├── __init__.py
    │   ├── templates
    │   │   └── myApp
    │   │       ├── base.html
    │   │       ├── base_menu.html
    │   │       ├── main.css
    │   ├── tests.py
    │   ├── urls.py
    │   └── views.py

In the tree above, the base.html is my main site. I want to include the main.css in my <head> of base.html, but the path isn't correct.

<link rel="stylesheet" href="main.css">

I have tried myApp/main.css, templates/myApp/main.css, so on... Not only can't I link my .css, I also want to use template inheritance for base_menu.html, same issue - the relative path isn't correct.

This is my main hindrance so far, please help. Thanks.

1

There are 1 answers

2
erewok On BEST ANSWER

Static Resources

We do not usually use Django to route to so-called "static" resources. The reasoning behind this is that it is a waste to have your server spin up a Python application which calls Django, just so you can locate and stream back a "static" or unchanging file. Django is for rendering dynamic responses (such as HTML with template tags in it where you are going to inject some content).

Anyway, with this in mind, a typical Django project is often organized something like this (I deleted the sub-directory inside myApp/templates to simplify things):

├── bootstrap-3.3.5-dist
└── mysite
    ├── db.sqlite3
    ├── myApp
    │   ├── admin.py
    │   ├── forms.py
    │   ├── __init__.py
    │   ├── static
    │   │   └── myApp
    │   │       ├── main.css
    │   │       ├── some.js
    │   ├── templates
    │   │    ├── base.html
    │   │    ├── base_menu.html
    │   ├── urls.py
    │   └── views.py

Then, in your setings.py you set up two important variables:

# This is the url that static routes will reverse to
STATIC_URL = '/static/'
# This is the place where static files actually live
STATIC_ROOT = '/location/for/storing/static/files'

After that, you can run the following command:

 $ python manage.py collectstatic

And Django will collect all the static files (inside the static directories inside the apps), and dump them all in directories at the location specified above.

Finally, this will allow you to reverse static urls in your templates like so:

{% load staticfiles %}
<link rel="stylesheet" href="{% static "myApp/main.css" %}">

This can be a bit confusing when you are serving content locally, because (contra what I said above) you will need a url route defined (if you are using django's runserver command), so that Django knows how to serve up the routes that will lead to these staticfiles.

See how to serve staticfiles in development and another stackoverflow answer


Template Inheritance

As for template inheritance, perhaps the missing insight is this one: stuff between template tags is rendered by Django and then that whole response is sent back.

Here's a quick demo:

Template File: myApp/templates/about.html

{% extends 'index.html' %}

{% block maincontainer %}
<div class="container">
  <p>
    Some content.
  </p>
</div>
{% endblock maincontainer %}

Template File: myApp/templates/index.html

<html>
  <head>
    {% load staticfiles %}
    <link rel="stylesheet" href="{% static "myApp/main.css" %}">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script>
    <link 
  </head>
  <body>
    {% include "base_menu.html" %}
    {% block maincontainer %}
    {% endblock maincontainer %}
  </body>
</html>

Here's what your Django view will do with these templates:

  1. You tell it to render about.html. It will parse and evaluate everything between the template tag indicators {% and %}.

  2. It sees you want this template to extend another template, index.html, so it magically locates that template and does step 1 on it.

  3. index.html includes the line include base_menu.html, so Django magically locates that template, does Step 1 and inserts all of its content where the include statement is.

  4. Django finds the template tag static blabla, so it does a urlreverse for the static file referenced there (meaning, it creates a complete url referring to the static file).

  5. Finally, after it's done with index.html and any other included templates, back in about.html, it sees you want to override a content block defined in index.html and so it replaces that chunk defined in index.html with the overridden chunk defined in about.html.

After that, it's done, and it sends the completed template, which is now simply a large string, back to whatever requested it. This may include something like the following tag:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script>

Your browser is responsible for figuring out what to do with this line. This is why your first attempt at including css failed: the browser was trying to locate that file on disk and didn't know where it was.