Dan
Dan

Reputation: 329

Django static files - error 404 on access attempt

I'm following the gettingstartedwithdjango.com tutorials but with newer versions of code. I'm on the second tutorial and I'm near the end but I'm unable to successfully style my site with CSS due to 404 errors serving static files content. I'm not exactly clear on why.

I'm running a Vagrant VM atop Windows 7 for development, /vagrant (in the VM) is mapped to C:/VAGRANT (in Win7). I created C:/VAGRANT/PROJECTS to contain all of my projects. I created a virtualenv for my project which sits in /home/vagrant/blog-venv (in the VM), the contents for that project from a code perspective sit in C:/VAGRANT/PROJECTS/microblog.

DIRECTORY STRUCTURE

PROJECTS
    static  # subdirs not auto-created yet as functionality not yet working
    uploads # subdirs not auto-created yet as functionality not yet working
    microblog
        manage.py <file>
        assets
            css
            fonts
            js
        microblog
            settings
            templates
                _layouts
        blog
            migrations
            templates
                blog

Based on the above directory structure, here are the relevant files and their contents:

/microblog/microblog/settings/__init__.py

from .base import *
try:
    from .local import *
except ImportError:
    pass

/microblog/microblog/settings/base.py

DEBUG = False
here = lambda * x: os.path.join(os.path.abspath(os.path.dirname(__file__)), *x)
PROJECT_ROOT = here("..")
root = lambda * x: os.path.join(os.path.abspath(PROJECT_ROOT), *x)
TEMPLATES = [
    {
    'DIRS': [
        root("templates")
    },
]
MEDIA_ROOT = root("..", "..", "uploads")
MEDIA_URL = ''
STATIC_ROOT = root("..", "..", "static")
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    root("..", "assets"),
]
STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]
ALLOWED_HOSTS = ['*']
DJANGO_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = []
LOCAL_APPS = [
    'blog',
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

/microblog/microblog/settings/local.py

DEBUG = True

/microblog/microblog/urls.py

from django.conf.urls import url, include
from django.conf.urls.static import static
from django.contrib import admin
import blog.urls
from . import views, settings
admin.autodiscover()
urlpatterns = [
    url(r'^$', views.HomepageView.as_view(), name="home"),
    url(r'^blog/', include(blog.urls, namespace="blog")),
    url(r'^admin/', admin.site.urls),
    # url(r'^static/(.*)$', 'django.views.static.serve', {'document_root': settings.base.STATIC_ROOT}),
    ]# + static(settings.base.STATIC_URL, document_root=settings.base.STATIC_ROOT)

/microblog/microblog/views.py

from django.views.generic import TemplateView
class HomepageView(TemplateView):
    template_name = "index.html"

/microblog/blog/views.py

from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post
class PublishedPostsMixin(object):
    def get_queryset(self):
        queryset = super(PublishedPostsMixin, self).get_queryset()
        return queryset.filter(published=True)
class PostDetailView(PublishedPostsMixin, DetailView):
    model = Post

/microblog/microblog/templates/_layouts/base.html

<!doctype html>
<html>
    <head>
        <title>{% block page_title %}{% endblock %}Microblog</title>
        <link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css">
    </head>
    <body>
        <div class="container">
            <h1>Welcome to my site!</h1>
            {% block page_content %}{% endblock %}
        </div>
    </body>
</html>

/microblog/microblog/templates/index.html

{% extends "_layouts/base.html" %}
{% block page_content %}
<a href="{% url 'blog:list' %}" class='btn'>Read my blog</a>
{% endblock %}

THE RUNSERVER ERROR I'M SEEING IS:

Performing system checks...
System check identified no issues (0 silenced).
December 23, 2015 - 11:13:14
Django version 1.9, using settings 'microblog.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

[23/Dec/2015 11:13:20] "GET / HTTP/1.1" 200 391
Not Found: /css/bootstrap.min.css
[23/Dec/2015 11:13:20] "GET /css/bootstrap.min.css HTTP/1.1" 404 2190

ATTEMPT AT PROBLEM DIAGNOSIS

Taking the 'Not Found' message from above, the path /css/bootstrap.min.css rather than /static/css/bootstrap.min.css has lead me to believe the problem is manifesting itself in the /microblog/microblog/templates/_layouts/base.html file here:

<link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css">

It appears that {{ STATIC_URL }} is not being interpreted to it's it set value of '/static/' and therefore the lookup path is /css/bootstrap.min.css rather than the expected /static/css/bootstrap.min.css. Yes, I have validated that the expected path and file does exist on the actual file system. Accordingly, when the page renders, it's just your standard HTML output without the CSS styling that I'm expecting. In the /microblog/microblog/templates/index.html file, the class="btn" is not working/rendering properly either.

I have read that this might have to do with the values of DEBUG and/or ALLOWED_HOSTS and of course I suspect my PATHS.

Let's deal with the paths first. It could be that best-practices dictate that I should adjust my current paths somewhat, please advise (lack of experience here), but for now I'm just following what I was told to do on the tutorial website. In that vein and since I'm not using hard-coded paths, I have confirmed the post-code-interpretation (base.py) of each of my paths, as well as the actual existence of each of these folders in my file system (see the DIRECTORY STRUCTURE block near the top of this post) as follows:

here() = /vagrant/PROJECTS/microblog/microblog/settings
PROJECT_ROOT = /vagrant/PROJECTS/microblog/microblog
root() = /vagrant/PROJECTS/microblog/microblog
TEMPLATES = [{'DIRS': = /vagrant/PROJECTS/microblog/microblog/templates }]
MEDIA_ROOT = /vagrant/PROJECTS/uploads
MEDIA_URL = ''
STATIC_ROOT = /vagrant/PROJECTS/static
STATIC_URL = '/static/'
STATICFILES_DIRS = [ /vagrant/PROJECTS/microblog/assets ]

With regards to DEBUG needing to be set to 'True', this is the case in my local.py file, which is imported when I run Django locally but not imported when I push code to production (local.py is excluded from production sync via .gitignore).

With regards to ALLOWED_HOSTS you'll see I currently have a value of '*' which may or may not be appropriate. I don't know, lack of experience.

I've noticed that running python manage.py collectstatic collects 0 files but I'm unsure why.

You'll notice that my /microblog/microblog/urls.py has some lines commented out of it, I've tried various things here to try to resolve the issue.

One further observation. The admin side of my project has the default styling typical of the Django Admin interface but this only became true when I enabled the STATICFILES_FINDERS called django.contrib.staticfiles.finders.AppDirectoriesFinder. I believe that's because this causes Django to go look where the files are for the admin app. Since these haven't been synced to the STATIC_ROOT location (because python manage.py collectstatic is failing), I'm assuming it's looking in the location where my VENV makes these files available (/home/vagrant/blog-venv/lib/python2.7/site-packages/django/contrib/admin). Can anyone confirm this? As for the front-end side of the project, that piece is obviously still having trouble.

I'm relatively new to Django and I'm very stuck on this problem. Essentially this boils down to getting static files working for my site as a whole and without that I'm kind of dead in the water as I will need static files functionality for other aspects of my project too. I don't know if there is a better way I should be troubleshooting or debugging. I sort of expect that I'd get a browser error page (since DEBUG=True) that would help me further narrow down the problem but I'm not getting this despite my DEBUG setting. I also don't know if there is a way to python manage.py runserver with parameters which would produce more debug output as to why I'm getting the 404.

Any assistance or advice with this problem as a whole will be greatly appreciated. If any further information is required, please request and I will post an update. Thank you in advance!

Upvotes: 1

Views: 2728

Answers (1)

janos
janos

Reputation: 124804

This is really the key point to look at:

[23/Dec/2015 11:13:20] "GET /css/bootstrap.min.css HTTP/1.1" 404 2190

The /static prefix is missing from /static/css/bootstrap.min.css.

As the documentation explains, you should include this in your template to use static files:

{% load staticfiles %}

And in modern versions of Django, instead of:

<link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css">

You should use:

<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">

This static template tag appeared in Django 1.4, and has been the preferred approach since.

Upvotes: 3

Related Questions