Django-compressor on s3 does not recognize and copy updated files - django

I am using Django, django-compressor, django-storages to put static files for my site onto s3. I am able to get them onto s3, but when I make a modification to a file, and then run collect static, it reports that there were no modified files.
I did just modify a file, so I'm wondering if this is a timestamp issue somewhere on the server, somewhere on s3, or an inconsistency somewhere else.
I've been looking at the source of collect static ( https://github.com/django/django/blob/master/django/contrib/staticfiles/management/commands/collectstatic.py#L286 )
and it seems it will give the message
Skipping ...
instead of
Copying
if it has already copied it, but it is not clear to me why it would think it is already copied if it us updated.
Maybe I am misunderstanding the docs on compressor or staticfiles, but it seems to me it should already handle collecting and processing the file when it is updated, but it still says (not modified).

I've run into a few issues with django-storages and the S3Boto backend. Try this for some troubleshooting...
Make sure you have python-dateutil installed (see this thread):
pip install python-dateutil==1.2
Make sure you have appropriate settings for django-compressor and django-storages:
AWS_PRELOAD_METADATA = True
COMPRESS_STORAGE = 's3_storages.StaticRootS3BotoStorage'

Related

Django ManifestStaticFilesStorage not loading the correct static files

I am using a combination of django-storages and ManifestStaticFilesStorage to server static files and media from S3.
class StaticStorage(ManifestFilesMixin, S3BotoStorage):
location = settings.STATICFILES_LOCATION
When I run collectstatic I can see the newest version of my JS file on S3 with the correct timestamp.
I can also see that file being referenced in the staticfiles.json manifest.
However looking at the site in the browser I am still seeing the old JS being pulled down, not the one in the manifest
What could be going wrong?
The staticfiles.json seems to be loaded once when the server starts up (from the S3 instance). If you run collectstatic while the server is running it has no way of knowing that there were changes made to S3. You need to restart the server after running collectstatic if changes have been made.
You can read this post for more infomation. In short:
By default staticfiles.json will reside in STATIC_ROOT which is the
directory where all static files are collected in. We host all our
static assets on an S3 bucket which means staticfiles.json by default
would end up being synced to S3.
So if your staticfiles.json being cached, your static files will be the old ones.
There are 2 ways to fix this:
Versionize staticfiles.json like you're already done with your static files
Keep staticfiles.json in local instead of S3

How to remove old static files while keep on serving until deployment succeeds?

I'm having an issue with static files piling up on S3 since i'm using the ManifestFilesMixin mixin to give static files a unique name (so clients are forced to load the new content). Since the name is different on each version (duhhh) it's written next to old version of the same file.
Then also i don't like to use the --clear flag on collectstatic since this will (i expect) remove the current files even when deployment has not succeeded yet.
I thought to manually run: python manage.py collectstatic --clear but this seems to not remove the old versions from the bucket?
Anyone thoughts on this?
Paul

Heroku/Django Do I need to turn on maintenance mode when collecting static files

I've noticed that when i run python manage.py collectstatic --noinput users start seeing scrambled pages. After following on this, i found out that during the process i get 404 on css files, hence the scrambled view.
I've read the docs on Heroku regarding maintenance mode and deploying process, and nothing indicates that i need to turn maintenance mode on when i do a deploy with collectstatic.
The process of collecting static files is quite slow(~20 mins). I'm using django-pipeline to minify and combine the static files (with hashes),and then upload them to Amazon S3.
Is this a normal behaviour? Or am i doing something wrong?
Is there any way to deploy, with collectstatic, without taking the site down?
If you're having issues with the speed. Perhaps you could just upload the static files using collect static locally (or on another server) instead of on Heroku, of course you'll have to set up a process for that, but at least you won't have to tie up an expensive server for 20 minutes just to upload files.
You could also use: https://github.com/antonagestam/collectfast which speeds up the collect process by using md5 hashes. This would really improve the speed since only the "newness" would get uploaded.

Make 'collectstatic' find updated files

Is there a way to make python manage.py collectstatic find updated static files? Currently, it is properly searching STATICFILES_DIRS and finding where I have my static files, but it only uploads new ones. If I modify a static file, it does not detect this. Does Django do this so we have to delete each file first, or is there an easy solution?
UPDATE:
Disclaimer - This issue has to do with external hosting on Amazon's S3 Storage
I had simply forgot to include AWS_PRELOAD_METADATA = True in my settings.py file.
Adding this setting in fixed the issue of collectstatic only finding new files. Also, I saw a major speed increase in syncing between the server and Amazon's S3.
If you are using S3 for storage, I found this answer to be quite helpful.

django-compressor not setting absolute CSS image paths on Heroku

I'm using django-compressor to concatenate and compress my CSS and JS files on this site. I'm serving static files from an S3 bucket.
On my local copy of the site, using a different S3 bucket, this all works perfectly. But on the live site, hosted on Heroku, it all works except the relative URLs for images in the CSS files do not get re-written.
eg, this line in the CSS file:
background-image: url("../img/glyphicons-halflings-grey.png");
gets rewritten to:
background-image:url('https://my-dev-bucket-name.s3.amazonaws.com/static/img/glyphicons-halflings-grey.png')
on my development site, but isn't touched on the live site. So the live site ends up looking in pepysdiary.s3.amazonaws.com/static/CACHE/img/ for the images (as it's relative to the new, compressed CSS file).
For now, I've put a directory at that location containing the images, but I can't work out why there's this difference. Both sites have this in their settings:
COMPRESS_CSS_FILTERS = [
# Creates absolute urls from relative ones.
'compressor.filters.css_default.CssAbsoluteFilter',
# CSS minimizer.
'compressor.filters.cssmin.CSSMinFilter'
]
And the CSS files are being minimised just fine... but it's like the other filter isn't being applied on the live site.
I recently ran into this issue on heroku, and running the latest version of django-compressor (1.3) does not solve the problem. I will provide the solution that I am using, as well as an explanation of the problems I ran into along the way.
The solution
I created my own 'CssAbsoluteFilter' that removes the settings.DEBUG check from the 'find' method like this:
# compress_filters.py
from compressor.filters.css_default import CssAbsoluteFilter
from compressor.utils import staticfiles
class CustomCssAbsoluteFilter(CssAbsoluteFilter):
def find(self, basename):
# The line below is the original line. I removed settings.DEBUG.
# if settings.DEBUG and basename and staticfiles.finders:
if basename and staticfiles.finders:
return staticfiles.finders.find(basename)
# settings.py
COMPRESS_CSS_FILTERS = [
# 'compressor.filters.css_default.CssAbsoluteFilter',
'app.compress_filters.CustomCssAbsoluteFilter',
'compressor.filters.cssmin.CSSMinFilter',
]
The absolute urls now always work for me whether DEBUG = True or False.
The Problem
The issue is connected to 'compressor.filters.css_default.CssAbsoluteFilter', your DEBUG setting, and the fact that heroku has a read-only file system and overwrites your app files every time you deploy.
The reason compress works correctly on your development server is because CssAbsoluteFilter will always find your static files when DEBUG = True, even if you never run 'collectstatic'. It looks for them in STATICFILES_DIRS.
When DEBUG = False on your production server, CssAbsoluteFilter assumes that static files have already been collected into your COMPRESS_ROOT and won't apply the absolute filter if it can't find the files.
Jerdez, django-compressor's author, explains it like this:
the CssAbsoluteFilter works with DEBUG = False if you've successfully provided the files to work with. During development compressors uses the staticfiles finder as a convenience so you don't have to run collectstatic all the time.
Now for heroku. Even though you are storing your static files on S3, you need to also store them on heroku (using CachedS3BotoStorage). Since heroku is a read-only file system, the only way to do this is to let heroku collect your static files automatically during deployment (see https://devcenter.heroku.com/articles/django-assets).
In my experience, running 'heroku run python manage.py collectstatic --noinput' manually or even in your Procfile will upload the files to S3, but it will NOT save the files to your STATIC_ROOT directory (which compressor uses by default as the COMPRESS_ROOT). You can confirm that your static files have been collected on heroku using 'heroku run ls path/to/collected'.
If your files have been collected on heroku successfully, you should be able to compress your files successfully as well, without the solution I provided above.
However, it seems heroku will only collect static files if you have made changes to your static files since your last deploy. If no changes have been made to your static files, you will see something like "0 of 250 static files copied". This is a problem because heroku completely replaces your app contents when you deploy, so you lose whatever static files were previously collected in COMPRESS_ROOT/STATIC_ROOT. If you try to compress your files after the collected files no longer exist on heroku and DEBUG = False, the CssAbsoluteFilter will not replace the relative urls with absolute urls.
My solution above avoids the heroku problem altogether, and replaces relative css urls with absolute urls even when DEBUG = False.
Hopefully other people will find this information helpful as well.
I've had this exact same problem for a month now, but this is fixed in the 1.3 release (3/18/13, so you were probably on 1.2), so just upgrade:
pip install -U django-compressor
The exact problem I gave up on working out, but it's related to Heroku and CssAbsoluteFilter being called but failing at _converter method. Looking at the 1.3 changelog, the only related commit is this: https://github.com/jezdez/django_compressor/commit/8254f8d707f517ab154ad0d6d77dfc1ac292bf41
I gave up there, life's too short.
Meanwhile this has been fixed in django-compressor 1.6. From the changelog:
Apply CssAbsoluteFilter to precompiled css even when compression is disabled
i.e. the absolute filter is run even with DEBUG = True.