8

Just to get this out of the way, if at all possible, I'd like to do this without nesting them all inside a directory with the app's name inside the app's static folder, it feels redundant. If it's the only way then such is life.

I am using:

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

and:

STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'

which compiles the JS and SASS when running collectstatic. These are both located in the APP_ROOT/static/ directory.

The only 'problem' is that it brings along all the source sass and js files alongside it. At some point I'm going to be pushing this all to S3 and I'd like to avoid that if possible.

I found that if you run:

python manage.py collectstatic -i sass -i js

It still compiles the JS and CSS files I specified, while leaving the rest of the 'source' files out. Unfortunately, it also ignores every js file in /admin/ as it matches /admin/js/ etc. I don't even know if that's likely to be a problem for this particular project, but I can foresee in the future other apps where I definitely will want to include static js/css kept in an app.

What I'd like to be able to do is something like:

python manage.py collectstatic -i app_name/sass -i app_name/js

And as I mentioned at the top the easy solution is just to prefix my static files in the folder with app_name/, much like how django.contrib.admin does it. At this point however, you end up with a directory structure of PROJECT_ROOT/app_name/static/app_name/[js|sass|img|data]/ and I think it should be possible to avoid the redundancy.

Then again, maybe it's the best option, so as to guarantee avoiding conflict with other apps?

I've looked into writing custom storages and finders, and I think it's possible to roll my own. I wanted to check here though first, to see if this is a problem someone else has solved, or, to get a reality check if the overwhelming response is to just add the prefix directory.

If I was to roll my own, the path I think I would take would be extending django.contrib.staticfiles.finders.AppDirectoriesFinder and overriding list(). I'm not yet positive this approach would work, I need to more trace how things progress from the collectstatic management command, so if anyone has done this or something simlilar before, or knows why it will/won't work, any help is appreciated.

Thanks!

JamesH
  • 165
  • 1
  • 9

2 Answers2

7

I managed to solve this by subclassing Django finders like this:

PYTHON 2.X

from django.contrib.staticfiles import finders
from django.conf import settings


def add_ignores(ignore_patterns):
    ignore = settings.STATICFILES_FINDERS_IGNORE

    if ignore:
        if ignore_patterns:
            ignore_patterns.extend(ignore)
        else:
            ignore_patterns = ignore

    return ignore_patterns


class FileSystemFinderIgnore(finders.FileSystemFinder):
    def list(self, ignore_patterns):
        return super(FileSystemFinderIgnore, self).list(add_ignores(ignore_patterns))


class AppDirectoriesFinderIgnore(finders.AppDirectoriesFinder):
    def list(self, ignore_patterns):
        return super(AppDirectoriesFinderIgnore, self).list(add_ignores(ignore_patterns))


class DefaultStorageFinderIgnore(finders.DefaultStorageFinder):
    def list(self, ignore_patterns):
        return super(DefaultStorageFinderIgnore, self).list(add_ignores(ignore_patterns))

PYTHON 3.X

from django.contrib.staticfiles import finders
from django.conf import settings


def add_ignores(ignore_patterns):
    ignore = settings.STATICFILES_FINDERS_IGNORE

    if ignore:
        if ignore_patterns:
            ignore_patterns.extend(ignore)
        else:
            ignore_patterns = ignore

    return ignore_patterns


class FileSystemFinderIgnore(finders.FileSystemFinder):
    def list(self, ignore_patterns):
        return super().list(add_ignores(ignore_patterns))


class AppDirectoriesFinderIgnore(finders.AppDirectoriesFinder):
    def list(self, ignore_patterns):
        return super().list(add_ignores(ignore_patterns))


class DefaultStorageFinderIgnore(finders.DefaultStorageFinder):
    def list(self, ignore_patterns):
        return super().list(add_ignores(ignore_patterns))

and adding this to my settings:

STATICFILES_FINDERS_IGNORE = [
    '*.scss',
    '*.js',
]
charliebeckwith
  • 1,449
  • 11
  • 29
Kukosk
  • 2,892
  • 1
  • 27
  • 30
  • Wow, thankyou for replying to this question, it has been a long time. Since then I've moved from Django though I never got around to solving this. Looks like you've got it, though I have no way to test. I'll take your word for it and if someone comes along and tells us otherwise well we can change things then. – JamesH Dec 10 '14 at 00:17
0

I'm new to django-pipeline, but I believe it now has pipeline.finders.FileSystemFinder and pipeline.finders.AppDirectoriesFinder to do exactly this.

See the section "If you want to exclude Pipelinable content from your collected static files" on https://django-pipeline.readthedocs.org/en/latest/storages.html.

Also, from the source code of 1.5:

class AppDirectoriesFinder(PatternFilterMixin, DjangoAppDirectoriesFinder):
    """
    Like AppDirectoriesFinder, but doesn't return any additional ignored
    patterns.

    This allows us to concentrate/compress our components without dragging
    the raw versions in via collectstatic.
    """

Note that at the time of writing, this results in empty compressed/minified content when DEBUG==True, see https://github.com/cyberdelia/django-pipeline/issues/418. I assume this will be fixed in future versions of django-pipeline.

Henrik Heimbuerger
  • 9,924
  • 6
  • 56
  • 69