Search code examples
djangodjango-static

Referencing image in CSS with relative path doesn't work in Django


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?


Solution

  • 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()