Opened 4 weeks ago

Closed 3 weeks ago

Last modified 3 weeks ago

#36562 closed Bug (wontfix)

`StaticFilesHandler` should respect `StaticFilesStorage.base_url` and treat `STATIC_URL` as a fallback

Reported by: Kamil Paduszyński Owned by:
Component: contrib.staticfiles Version: 5.2
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

As follows from the official docs one can specify base_url in storage's "OPTIONS". StaticFilesStorage subclasses from FileSystemStorage, so one could expect that when setting:

# Storages

STORAGES = {
    "staticfiles": {
        "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
        "OPTIONS": {
            "base_url": "static/",
        },
    },
}

everything should work without touching STATIC_URL. However, we get:

django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the required STATIC_URL setting.

Would it be possible to change this behavior? IMHO, such change would make setting up storages (even in development) more centralized, thus cleaner.
Of course, this would not be a breaking change, since STATIC_URL could be still returned when base_url is not explicitly set.

I believe that this would require updating get_base_url method of the static files handler mixin:

https://github.com/django/django/blob/ad4a9e0f3b1de261409bc083aa49dba705531824/django/contrib/staticfiles/handlers.py#L29-L31

Change History (5)

comment:1 by Natalia Bidart, 4 weeks ago

Resolution: worksforme
Status: newclosed
Type: UncategorizedBug

Hello Kamil Paduszyński, thank you for taking the time to create this ticket.

I invested some time investigating this, and I can't reproduce the error you shared. I tried various local settings and they all work. So then, I wrote a test that shows how the base_url from OPTIONS gets used by the StaticFilesStorage. This test is passing in main:

  • tests/staticfiles_tests/test_checks.py

    diff --git a/tests/staticfiles_tests/test_checks.py b/tests/staticfiles_tests/test_checks.py
    index b9ac486ed1..d8427b0413 100644
    a b class StoragesCheckTests(SimpleTestCase):  
    164164    def test_staticfiles_no_errors(self):
    165165        errors = check_storages(None)
    166166        self.assertEqual(errors, [])
     167
     168    @override_settings(
     169        STATIC_URL=None,
     170        STORAGES={
     171            "staticfiles": {
     172                "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
     173                "OPTIONS": {"base_url": "override/"},
     174            }
     175        },
     176    )
     177    def test_base_url_in_storages_options_without_static_url(self):
     178        from django.core.files.storage import storages
     179        storage = storages["staticfiles"]
     180        self.assertEqual(storage.base_url, "override/")

Because of the above, I'll close as worksforme. If you still have this issue, before re-opening please be sure to provide a failing test case for the Django test suite or provide a minimal Django test project for us to reproduce.

Last edited 4 weeks ago by Natalia Bidart (previous) (diff)

comment:2 by Kamil Paduszyński, 4 weeks ago

Sorry for misunderstanding. Let me explain it in more detail.

Setting STATIC_URL=None and putting the actual URL into base_url options results in passing system checks (the check command).

The mentioned error is raised when I execute the runserver command (the one overloaded from the staticfiles app). Your tests don't account for running commands, I guess. I also checked that the problem (I don't want to call that a bug for now) raises only in debugging mode DEBUG = True.

Sample project: https://github.com/paduszyk/ticket_36562.

My tests:

from django.core.exceptions import ImproperlyConfigured
from django.core.management import call_command
from django.test import TestCase
from django.test.utils import override_settings


@override_settings(
    DEBUG=True,
    STORAGES={
        "staticfiles": {
            "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
            "OPTIONS": {
                "base_url": "static/",
            },
        },
    },
    STATIC_URL=None,
)
class TestStoragesIfBaseURLandStaticURLNone(TestCase):
    def test_check_does_not_raise_error(self):
        try:
            call_command("check")
        except Exception:
            self.fail()

    def test_runserver_raises_improperly_configured(self):
        with self.assertRaises(ImproperlyConfigured):
            call_command("runserver", "--noreload")

comment:3 by Kamil Paduszyński, 4 weeks ago

Resolution: worksforme
Status: closednew

in reply to:  3 comment:4 by Natalia Bidart, 3 weeks ago

Resolution: wontfix
Status: newclosed

Replying to Kamil Paduszyński:

Thanks for the follow up and the extra details. After reviewing this I think this should be closed as wontfix. A few points:

  • The StaticFilesHandler is for development only. It is the helper that runserver uses when DEBUG=True. In production you should always serve static files with a proper web server or CDN or similar. The handler itself is undocumented, which is a sign it is an internal detail and not part of the public API.
  • The handler has no direct relation to the storage backend. The storage controls where files are stored and what URLs are returned. The handler is a WSGI wrapper that serves requests. They both care about static files but they are independent..
  • The parallel you mention between FileSystemStorage and StaticFilesStorage does not apply here for handlers. There is no handler version of each storage, they are different layers.

Because of the above, making the handler honor the base_url from storage does not seem right. It would blur two separate concepts and could confuse the setup. Keeping STATIC_URL as the single knob that the handler checks is simpler and keeps the dev helper separated from storage configuration.

comment:5 by Natalia Bidart, 3 weeks ago

Summary: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a fallback`StaticFilesHandler` should respect `StaticFilesStorage.base_url` and treat `STATIC_URL` as a fallback
Note: See TracTickets for help on using tickets.
Back to Top