8

I am using Django 1.3 with built-in static app.

My static folder structure is like this:

static/
    css/
       main.css
       img/
    js/

So I tried to reference images under static/css/img/ folder from CSS like this:

background:url('img/btn_white.gif') repeat-x;

But the images don'e show up. When I inspect elements in Chrome, I found the image path to be http://localhost/mysite/static/css/main.css/img/btn_white.gif/

Which is very wierd since this relative path should have referenced static/css/ folder instead of main.css. So I tried to change path to be url('../img/btn_white.gif'), and it works in Chrome and Firefox but not in IE.

I am pretty sure this problem is related to Django, because in my pure HTML/CSS, this relative path works just fine. I also tried to put css in media folder and the problem is the same.

My settings related to static app:

in settings.py:

STATIC_ROOT = os.path.join(os.path.dirname(__file__),'static').replace('\\','/')
STATIC_URL = 'http://localhost/mysite/static/'

in urls.py:

(r'^static/(?P<path>.*)/$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),

Related question: Is a relative path in a CSS file relative to the CSS file?

Community
  • 1
  • 1
Yifu
  • 265
  • 1
  • 4
  • 8

1 Answers1

9

The problem is caused by your URLconf, specifically the pattern:

r'^static/(?P<path>.*)/$'

This means that a the URL must end in a forward slash for it to match this pattern. i.e. the following URL will not match: (because it doesn't have a trailing slash)

/mysite/static/css/main.css

The weird thing, is that it does work. The reason for this is Django's APPEND_SLASH setting:

When set to True, if the request URL does not match any of the patterns in the URLconf and it doesn't end in a slash, an HTTP redirect is issued to the same URL with a slash appended. Note that the redirect may cause any data submitted in a POST request to be lost.

So when your browser makes a request to:

/mysite/static/css/main.css

…Django will fail to match it against any of the URLs, and will issue a redirect to: (because APPEND_SLASH defaults to True)

mysite/static/css/main.css/

This new request will succeed and your browser will now be able to download the CSS file, however the CSS file's resource URL now ends with a slash. When your browser processes the CSS rules and comes across:

background:url('img/btn_white.gif') repeat-x;

It will attempt to join that relative URI to the URI of the CSS resource. e.g.:

/mysite/static/css/main.css/ + img/btn_white.gif = /mysite/static/css/main.css/img/btn_white.gif

This will fail, so your browser will get a redirect to: (again because of APPEND_SLASH)

/mysite/static/css/main.css/img/btn_white.gif/

But obviously that too will fail.

Solutions

Change your URL pattern to the following: (note the removed trailing / in the pattern)

(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),

Or use one of the recommended methods:

from django.conf import settings

if settings.DEBUG:
    urlpatterns += patterns('django.contrib.staticfiles.views',
        url(r'^static/(?P<path>.*)$', 'serve'),
    )

…or:

from django.contrib.staticfiles.urls import staticfiles_urlpatterns

# ... the rest of your URLconf here ...

urlpatterns += staticfiles_urlpatterns()
bradley.ayers
  • 37,165
  • 14
  • 93
  • 99
  • That makes sense - I've had more problems with when you should or should not have trailing slashes... – John C Jun 12 '11 at 21:33
  • Hopefully my write-up makes it a bit clearer what's going on :) – bradley.ayers Jun 12 '11 at 21:34
  • Thanks, that's exactly the problem! I solved it by eliminating the slash in my original url setting. But for the two suggested ways you gave me, the first one won't work. The second one has import error in Django 1.3. – Yifu Jun 13 '11 at 15:59
  • @Yifu it works fine for me on Django 1.3, open a new question if you want to explore that problem. – bradley.ayers Jun 13 '11 at 21:57
  • I've spent much more time slashing Django than I'd like. Nice explanation +1 – Daniel Dec 12 '13 at 07:48