serve static file with herokku, django and wsgi - django

I was working to deploy a website to heroku. The site seems to recognize my static files if I run with django's builtin server namely "runserver". However if I run with gunicorn The static files cannot be recognized. I was wondering if there's any special setting I need to tweak to magically make the recognition happen. Can anyone enlighten me how these two commands differ specifically or does it have anything to do with wsgi staff?
Thanks!!!
This is how I do with runserver in Procfile, which is quite neat.
web:python manage.py runserver
And here's what I do with gunicorn in Procfile, which is a mess
web: gunicorn some.dotted.path.to.mywsgi:application
UPDATE
Luckily I worked around this problem by including the following line to my urls.py. Though I know it's not a perfect solution because in real word, you need to shutdown DEBUG. but as for now in development. it's working well. Can anyone explain what this line magically do?
if settings.DEBUG:
urlpatterns += patterns('django.contrib.staticfiles.views',
url(r'^static/(?P<path>.*)$', 'serve'),
)

Serving static files on Heroku with Django is a bit tricky. Assuming you're using the 'staticfiles' app, you have to run 'collectstatic' to collect your static files after deploying. The problem with Heroku is that running 'collectstatic' in the shell will actually run in a new dyno, which disappears as soon as it's finished.
One potential solution is outlined here:
Basically, the idea is to combine a few commands in your Procfile so that 'collectstatic' is run during the dyno spinup process:
web: python my_django_app/manage.py collectstatic --noinput; bin/gunicorn_django --workers=4 --bind=0.0.0.0:$PORT my_django_app/settings.py
You also have to add the 'static' views to your urls.py (see https://docs.djangoproject.com/en/dev/howto/static-files/#serving-files-uploaded-by-a-user, but duplicate for STATIC_URL and STATIC_ROOT). It's worth noting that the Django docs recommend against using this in production.
This solution isn't ideal though, since you are still using your gunicorn process to serve static files. IMHO the best approach to dealing with static files on Heroku is to host them on something like S3.

Related

Django: Static files not working in production environment

I tried all the ways to deploy static files in production environment but still the static files are not appearing. I don't know whats wrong.
Please help me in deploying static files(like css, javascripts, images, etc.).
I've tried using many different ways from many sources.But all in vain
Is there any easy way or can you please explain the traditional ways. Can you please explain the steps that you followed?
At last i got an easy way to deploy static files in production environment. Therefore answering my own question.
For all the newbies like me who are feeling helpless in this case see this:-
https://github.com/kennethreitz/dj-static
1st Step: Install this using following command in terminal
$ sudo pip install dj-static
This is a simple Django middleware utility that allows you to properly serve static assets from production with a WSGI server like Gunicorn.
2nd Step:- Just set
#In settings.py file (to set production environment.)
Debug= False
3rd Step:- Configure your static assets in settings.py:
#add your path to STATIC_ROOT
STATIC_ROOT = 'staticfiles'
STATIC_URL = '/static/'
4th Step:-
And copy the lines written below and add them to wsgi.py
Do not remove any other lines. Just add these
from django.core.wsgi import get_wsgi_application
from dj_static import Cling
application = Cling(get_wsgi_application())
And its done. Now test it.
Note: Only using Apache(2.4) as web server. Nothing else to support apache.
Hope this helps other people too.
if you have some more ways or simplified version of the traditional way please do share it.

Why won't newly installed Django app with NGINX serve static assets properly?

I have a Mac running OS X 10.9.3. I am trying to setup a Django application backed by a PostgreSQL database served by gunicorn, with static assets served by NGINX. I'm an old hand at Django with MySQL running with the developement server (manage.py runserver). But I'm new to setting it up with virtualenv, gunicorn and NGINX. So I'm following the instructions here.
My Django Project is being served successfully at localhost:3026. As a test of the database connectivity, I wanted to take a look at the Django Admin interface. I visited localhost:3026/admin/
I have included a screenshot below.
Why does this admin page look so ugly? It lacks the neccessary graphical interface and css that it is supposed to have? It looks like NGINX is not properly serving up those static assets. How can I troubleshoot and fix this issue?
EDIT:
After I posted this question, I did python manage.py collectstatic. That went and successfully copied all the static files to where they were supposed to (I think?) live in /opt/myenv/static. You can see the output of that command here. I then re-started gunicorn and nginx. I thought that would fix it. But unfortunately it didn't. The issue remains. In my Django settings.py file, I have configured the STATIC variables as follows:
STATIC_ROOT = "/opt/myenv/static/"
STATIC_URL = '/static/'
Try run command,
python manage.py collectstatic
If the commands executes successfuly, the static file would be generated in your project path, and then if you config the right static path, the web page will be correct.

Why isn't collectstatic being run automatically when I deploy my Django app to Heroku?

I've followed the official Heroku docs on Django and Static Assets; I've installed dj-static and added it to my requirements.txt file, properly configured all the variables in my settings.py file:
STATIC_ROOT = os.path.join(CONFIG_ROOT, 'served/static/')
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(CONFIG_ROOT, 'static'),
)
And this is what my wsgi.py looks like:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_django_project.settings")
from django.core.wsgi import get_wsgi_application
from dj_static import Cling
application = Cling(get_wsgi_application())
The contents of Procfile:
web: gunicorn --bind 0.0.0.0:$PORT my_django_project.wsgi:application
In the docs, it says that "collectstatic is run automatically when it is configured properly." But when I navigate to my site there's clearly no css.
I've tried debugging using heroku run, but that just copies the static files as expected.
I've noticed that when I include the collectstatic command in my Procfile, i.e.
web: python my_django_project/manage.py collectstatic --noinput ; gunicorn -b 0.0.0.0:$PORT my_django_project.wsgi:application
...that works as expected, and the static files are served.
However what's strange about that is when I run heroku run bash and look inside the directory that STATIC_ROOT is pointing to, there's nothing there! In fact, the entire served/ directory is missing, and yet, the static files are still being served!
I'd still like to know why isn't collectstatic being run automatically though -- like mentioned in the docs -- when I deploy my Django app to Heroku.
It looks like you might be using a specific settings module for Heroku/production. Further, you've set the environment variable DJANGO_SETTINGS_MODULE to point to this settings module (and that way, when the app runs, Django knows to use that one and not, say, your default/development one). Finally, you've probably configured static asset settings in Heroku/production settings module (perhaps, STATIC_ROOT).
Okay, so if this is all correct, then here is the issue: heroku environment variables are only set at serve-time, not at compile-time. This is important because collectstatic is a compile-time operation for Heroku. (Heroku goes through 2 stages when you push: 1) compiling, which involves setting the application up (collectstatic, syncdb, etc) 2) serving, the normal operation of your application).
So, essentially, you've done everything correctly, but Heroku hasn't exposed your environment variables, including your specification of a different settings module, to collectstatic.
To have your environment variables set to compile-time, enable Heroku's user-env-compile lab feature like this:
heroku labs:enable user-env-compile
I think this is a silly thing to do by default, and would be interested in hearing why Heroku thought it was a good idea.
Have you tried adding the user_env_compile setting to your heroku config?
heroku labs:enable user-env-compile
With that enabled collectstatic should be run whenever you deploy to heroku automatically.
I am using the heroku python buildpack with dokku, and collectstatic was not being run because it had no execute permission. They fixed that in a recent commit (Dec 13, 2013), so it should work now.

Running Django with Gunicorn - Best Practice

There are 3 ways to run a django application with gunicorn:
Standard gunicorn + wsgi (ref django doc)
gunicorn project.wsgi:application
Using gunicorn django integration (ref gunicorn doc and django doc):
python manage.py run_gunicorn
Using gunicorn_django command (ref gunicorn doc)
gunicorn_django [OPTIONS] [SETTINGS_PATH]
Django's documentation suggests using 1., which is not even listed as an option on Gunicorn documentation.
Is there any best practice on the best way to run a django app with gunicorn, and what are the foreseable advantages/disadvantages of these different solutions?
Taking a glimpse at gunicorn's code it looks like they pretty much all do the same: 2. seems to be creating a wsgi app using django's internals, and 3. uses 2.
If that's the case, I wouldn't even understand what's the reason for not simply using "1." all the time, especially since a wsgi.py file is autocreated for you since django 1.4; if that's true maybe simply a documentation improvement should be suggested...
Also, best practice for gunicorn settings with django would be great. Using 1., does it make sense to set some defaults in the wsgi file and avoid additional settings?
References:
Should I use django-gunicorn integration or wsgi? only concerns choices 1. and 3., there's no hint for the settings and the answer gives no rationale
Deploying Django with gunicorn and nginx give some broader information but is not strictly related nor answer this question
Django Gunicorn wsgi about version "4", which is launching gunicorn -c configfile and configfile will point to django_settings to django
Django WSGI and Gunicorn is just a bit confusing :) mixing up 1. and 3. Of course wsgi.py is used only with 1.
After checking out I'd say that the best way is using gunicorn + wsgi
$ gunicorn project.wsgi:application
It's now both confirmed in gunicorn docs: if you run Django 1.4 or newer, it’s highly recommended to simply run your application with the WSGI interface using the gunicorn command and django as linked above.
It also avoids adding gunicorn as installed app, which means it's not a requirement to install gunicorn to test your app which might be useful from time to time.
About Settings
The Django settings file to be used can be passed through an ENV variable, or customized in the wsgi.py file. I sometimes create several wsgi.py files if I have multiple settings (eg. multiple websites) that have to run from the same project - See Django Doc for more info.
A one-liner solution that does not require any new file from Carl's comment:
DJANGO_SETTINGS_MODULE=project.settings.prod gunicorn project.wsgi:application
sounds like a nicer way (though I'll probably end up writing it in some shell commands to make it easy to "remember").
Gunicorn settings can be passed as -c settings_file, but I'm exploring other ways and will try to update this answer if I find any. Using environment variables seems a workaroud, but only for limited cases
In particular it would be nice to get/share some settings between django and gunicorn; gunicorn documentation says:
Currently, only Paster applications have access to framework
specific settings. If you have ideas for providing settings to WSGI
applications or pulling information from Django’s settings.py feel
free to open an issue to let us know.
(Update: haven't found any smarter way, but after all env variables are enough for my most-common cases).
I guess using run_gunicorn is the way to go, it's also the simplest way to use it.
It's basically the same as usign gunicorn project.wsgi:application but needs gunicorn to be added to INSTALLED_APPS so that django recognizes the run_gunicorn command, therefore it's probably not the default way...
Using gunicorn_django is more or less deprecated, as the documentation also states here...
Found a solution that works both for manage.py (on local machine) and with gunicorn
create a file that has all the evniroment stuff
# mysite/settings/set_env.py
import os
_environment = os.getenv('environment', None)
if _environment == "production":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings.production")
elif _environment == "development":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings.development")
else:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings.local")
and import this file in your manage.py and wsgi.py files, no need to change anything else in gunicorn execution

Heroku collectstatic not run during deployment

I have a django app that I am successfully deplying to heroku. When I dry-run the collectstatic command locally everything works fine.
python manage.py collectstatic --dry-run --noinput
....
Pretending to copy '/Users/hari/.virtualenvs/bsc2/lib/python2.7/site-packages/django/contrib/admin/static/admin/js/admin/ordering.js'
Pretending to copy '/Users/hari/.virtualenvs/bsc2/lib/python2.7/site-packages/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js'
71 static files copied.
Despite this ..my django admin staticfiles do not get used and I get a bare-bones django admin site on heroku with Debug set to False.
If I set Debug to True I get a "rich" admin site on heroku. With Debug set to True or False "git push heroku master " command terminal output does not have anything about collecting staticfiles.
I tried the example "helloworld" application that uses gunicorn from Heroku and that did display the "collecting static" messages.I also tried inserting this code snippet into my urls.py. But that too does not help.
from django.conf import settings
if not settings.DEBUG:
urlpatterns += patterns('',
(r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}),
Next, I tried adding the following to my heroku config
heroku config:add DISABLE_COLLECTSTATIC=0
But that too did not show my django admin site with all the styles.
Finally I tried switching to gunicorn with my Procfile and that also did not show the admin styles. Only setting Debug=True works to show my admin styles.
I tried this with Django 1.4.2 and 1.5.1 on Heroku and neither is showing me a "normal" admin site. Is there any way out to have my admin files on heroku without going the S3 route.
command terminal output does not have anything about collecting staticfiles.
Looking at heroku-buildpack-python:bin/steps/collectstatic it seems that it tries to do a collectstatic --dry-run --noinput and pipes it's output to /dev/null before displaying the -----> Collecting static files message. This means that if there is an error there that is not present on your local box, you will never see the error on heroku : it will silently fail. (The best kind of failure ;)
have you tried running a one-off worker to test out the collectstatic command and see if it's a problem in their environment?
heroku run python manage.py collectstatic --dry-run --noinput
If this fails, it will give you an error or traceback to look into and further diagnose the issue.
Check this out Django and Static Assets. It seems to be updated recently and you can serve static files nicely using this dj-static package.
Try these three things:
Create this Heroku config variable: DJANGO_SETTINGS_MODULE with a
value of myapp.settings.prod--or as appropriate for your Heroku settings file
Use Whitenoise as described in the Heroku docs:
https://devcenter.heroku.com/articles/django-assets
Check it in and redeploy your dyno: git push heroku master
I found I was missing the first item, the DJANGO_SETTINGS_MODULE" e.g., Command line collectstatic would work but that didn't matter b/c it was an ephemeral dyno
throwing it out there, since dumb mistakes happen:
I spent much time trying to figure out why my module wasn't found on heroku when it was working fine locally, only to realize it was ignored by an entry into .slugignore