Note that there are some explanatory texts on larger screens.

plurals
  1. PODeploying Django app on Heroku: Can I manually set environment variables in the .env file? Do I need to install tools like autoenv, heroku-config...?
    primarykey
    data
    text
    <h2>My goal:</h2> <p>I intend to follow "The Twelve-Factor App" methodology for building my Django app on Heroku.</p> <h2>Introduction:</h2> <p>I'm following the "Getting Started with Django on Heroku" quick start guide. At the moment I have the following directory structure: </p> <pre><code>~/Projects/ hellodjango_rep/ .env (empty) .git .gitignore Procfile requirements.txt hellodjango/ manage.py hellodjango/ __init__.py settings/ urls.py wsgi.py </code></pre> <p>I installed django-toolbelt, created my simple Django application, started the process in my Procfile... Everything seemed to be working fine, but the problems started when I configured the application for the Heroku environment and added: </p> <pre><code>import dj_database_url DATABASES['default'] = dj_database_url.config() </code></pre> <p>to the bottom of my settings.py file. </p> <p>I pushed my application’s repository to Heroku, visited the app in my browser with <code>$ heroku open</code> successfully, but locally: <code>dj_database_url.config()</code> <strong>returned an empty dictionary</strong>.</p> <h2><strong>Locally:</strong></h2> <p>OS X 10.8.4<br> pip==1.4.1<br> virtualenv==1.10.1<br> virtualenvwrapper==4.1.1<br> wsgiref==0.1.2<br> Postgres.app running on Port 5432</p> <p>Environment variables:</p> <pre><code>mac-pol:hellodjango_rep oubiga$ python &gt;&gt;&gt; import os &gt;&gt;&gt; os.environ { 'PROJECT_HOME': '/Users/oubiga/Projects'... 'PATH': '/usr/local/heroku/bin:/usr/local/share/python:/usr/local/bin:/Applications/Postgres.app/Contents/MacOS/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/git/bin'... 'HOME': '/Users/oubiga'... 'WORKON_HOME': '/Users/oubiga/Envs'... 'VIRTUALENVWRAPPER_HOOK_DIR': '/Users/oubiga/Envs'... 'PWD': '/Users/oubiga/Projects/hellodjango_rep' } </code></pre> <h2>hellodjango_venv:</h2> <p>Django==1.5.2<br> dj-database-url==0.2.2<br> dj-static==0.0.5<br> django-toolbelt==0.0.1<br> gunicorn==18.0<br> psycopg2==2.5.1<br> static==0.4</p> <p>This is what I have in my wsgi.py file: </p> <pre><code>import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hellodjango.hellodjango.settings") from django.core.wsgi import get_wsgi_application from dj_static import Cling application = Cling(get_wsgi_application()) </code></pre> <p>This is what I have in my manage.py file:</p> <pre><code>import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hellodjango.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) </code></pre> <p>This is what I have in my Procfile: </p> <pre><code>web: gunicorn hellodjango.hellodjango.wsgi </code></pre> <p>Environment variables: </p> <pre><code>(hellodjango_venv)mac-pol:hellodjango_rep oubiga$ python hellodjango/manage.py shell &gt;&gt;&gt; import os &gt;&gt;&gt; os.environ { 'PROJECT_HOME': '/Users/oubiga/Projects'... 'PATH': '/Users/oubiga/Envs/hellodjango_venv/bin:/usr/local/heroku/bin:/usr/local/share/python:/usr/local/bin:/Applications/Postgres.app/Contents/MacOS/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/git/bin', 'HOME': '/Users/oubiga'... 'WORKON_HOME': '/Users/oubiga/Envs'... 'VIRTUAL_ENV': '/Users/oubiga/Envs/hellodjango_venv'... 'VIRTUALENVWRAPPER_HOOK_DIR': '/Users/oubiga/Envs'... 'PWD': '/Users/oubiga/Projects/hellodjango_rep'... 'DJANGO_SETTINGS_MODULE': 'hellodjango.settings' } </code></pre> <h2>On Heroku:</h2> <p>Environment variables: </p> <pre><code>(hellodjango_venv)mac-pol:hellodjango_rep oubiga$ heroku run python hellodjango/manage.py shell &gt;&gt;&gt; import os &gt;&gt;&gt; os.environ { 'DATABASE_URL': 'postgres://dbuser:dbpassword@ec2-184-73-162-34.compute-1.amazonaws.com:5432/dbname', 'HEROKU_POSTGRESQL_ORANGE_URL': 'postgres://dbuser:dbpassword@ec2-184-73-162-34.compute-1.amazonaws.com:5432/dbname', 'LIBRARY_PATH': '/app/.heroku/vendor/lib', 'PWD': '/app'... 'DJANGO_SETTINGS_MODULE': 'hellodjango.settings', 'PYTHONHOME': '/app/.heroku/python'... 'PYTHONPATH': '/app/'... 'DYNO': 'run.9068', 'LD_LIBRARY_PATH': '/app/.heroku/vendor/lib'... 'HOME': '/app', '_': '/app/.heroku/python/bin/python', 'PATH': '/app/.heroku/python/bin:/usr/local/bin:/usr/bin:/bin'... } (hellodjango_venv)mac-pol:hellodjango_rep oubiga$ heroku config === damp-dusk-5382 Config Vars DATABASE_URL: postgres://dbuser:dbpassword@ec2-184-73-162-34.compute-1.amazonaws.com:5432/dbname HEROKU_POSTGRESQL_ORANGE_URL: postgres://dbuser:dbpassword@ec2-184-73-162-34.compute-1.amazonaws.com:5432/dbname </code></pre> <h2>Research:</h2> <p><strong><em>Store config in the environment:</em></strong> from The Twelve-Factor App </p> <p>@adamwiggins wrote:</p> <blockquote> <p><strong>The twelve-factor app stores config in environment variables</strong>... Env vars are easy to change between deploys without changing any code; unlike config files.</p> </blockquote> <p><strong><em><code>dj_database_url.config()</code> is returning an empty object:</em></strong> from Heroku Forums</p> <p>@chrisantonick replied: </p> <blockquote> <p>... dj_database_url.config() gets the Postgres credentials from the Heroku environment variables. <strong>But, on your local machine, those variables aren't there</strong>. You have to put them in your /venv/bin/activate shell script... put the variables in there. something like<br> DATABASE_URL = "xxx"<br> export DATABASE_URL<br> For each thing it needs. Then... "deactivate"... and ..."activate" again to restart it.</p> </blockquote> <p><strong><em>Getting Started with Django and Heroku instructions raised ImproperlyConfigured error:</em></strong> from Heroku Forums </p> <p>@jwpe replied:</p> <blockquote> <p>... dj-database-url is a great utility, as it allows you to use exactly the same settings.py code in your development and production environments, as recommended in the "12 factor app principles"... what dj_database_url.config() is doing is looking for the DATABASE_URL environment variable, and then parsing it into Django's preferred format... if you haven't manually created and promoted a postgres DB on Heroku, DATABASE_URL will not be present and the ImproperlyConfigured error will be raised. Setting the default for dj_database_url.config() as your local DB URL is one way to make sure that your application will work in a development environment. However, it is not necessarily the only way. Perhaps <strong>a better alternative is to manually set DATABASE_URL in your local .env file. Then, when running your app locally using Foreman, it will be loaded as an environment variable and dj_database_url will find it.</strong> So your .env would contain: </p> </blockquote> <p><code>DATABASE_URL=postgres://user:pass@localhost/dbname</code><br> </p> <blockquote> <p>Meaning that in <code>settings.py</code> you would only need to have:<br></p> </blockquote> <p><code>DATABASES['default']= dj_database_url.config()</code><br> </p> <blockquote> <p>...The advantage of using a local environment variable instead of a single, hard-coded default is that your code will run in any environment where the DATABASE_URL is set. <strong>If you change the name of your local DB, or want to run your code on a different dev machine, you only need to update your .env file instead of tinkering with settings.py</strong>. </p> </blockquote> <p><strong><em>How to manage production/staging/dev Django settings?:</em></strong> from Heroku Forums </p> <p>@rdegges replied:</p> <blockquote> <p>... trying to get your application to behave in a way such that:</p> <ul> <li>When you're running the app on your laptop, it uses your local Postgres server.</li> <li>When you're running the app on your staging Heroku app, it uses the Postgres server addon.</li> <li>When you're running the app on your production Heroku app, it uses the Postgres server addon. </li> </ul> <p>The best way to accomplish this is by using environment variables!… Environment variables are the most elegant (and scalable) way to handle application configuration between different environments… Instead of having many settings files, define a single file: settings.py, and have it make use of environment variables to pull service information and credentials... On Heroku, you can set environment variables manually by running:</p> <pre><code>$ heroku config:set SOME_VARIABLE=some_value </code></pre> <p>... there's always Kenneth Reitz's great autoenv tool. This lets you define a simple .env file in your project directory… And each time you enter your project directory, those environment variables will be automatically set so that you don't have to do anything special! Just run your project and everything will work as expected: <code>python manage.py runserver</code></p> </blockquote> <h2>As a First Attempt:</h2> <p>I manually set <code>DATABASE_URL</code> in my .env file: <code>DATABASE_URL=postgres://dbuser:dbpassword@ec2-184-73-162-34.compute-1.amazonaws.com:5432/dbname</code></p> <p>But when I run <code>$ foreman start</code> command:<br></p> <pre><code>(hellodjango_venv)mac-pol:hellodjango_rep oubiga$ foreman start 17:25:39 web.1 | started with pid 319 17:25:39 web.1 | 2013-09-11 17:25:39 [319] [INFO] Starting gunicorn 18.0 17:25:39 web.1 | 2013-09-11 17:25:39 [319] [INFO] Listening at: http://0.0.0.0:5000 (319) 17:25:39 web.1 | 2013-09-11 17:25:39 [319] [INFO] Using worker: sync 17:25:39 web.1 | 2013-09-11 17:25:39 [322] [INFO] Booting worker with pid: 322 </code></pre> <p>and tried to open my app in the browser <code>http://0.0.0.0:5000</code>:</p> <pre><code>17:26:59 web.1 | 2013-09-11 10:26:59 [322] [ERROR] Error handling request 17:26:59 web.1 | Traceback (most recent call last): 17:26:59 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 131, in handle_request 17:26:59 web.1 | respiter = self.wsgi(environ, resp.start_response) 17:26:59 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/dj_static.py", line 59, in __call__ 17:26:59 web.1 | return self.application(environ, start_response) 17:26:59 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 236, in __call__ 17:26:59 web.1 | self.load_middleware() 17:26:59 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 53, in load_middleware 17:26:59 web.1 | raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e)) 17:26:59 web.1 | ImproperlyConfigured: Error importing middleware django.contrib.auth.middleware: "dlopen(/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/psycopg2/_psycopg.so, 2): Library not loaded: @loader_path/../lib/libssl.1.0.0.dylib 17:26:59 web.1 | Referenced from: /Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/psycopg2/_psycopg.so 17:26:59 web.1 | Reason: image not found" </code></pre> <p>However, <code>dj_database_url.config()</code> returned: </p> <pre><code>{ 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'dbname', 'HOST': 'ec2-23-21-196-147.compute-1.amazonaws.com', 'USER': 'dbuser', 'PASSWORD': 'dbpassword', 'PORT': 5432 } </code></pre> <h2>As a Second Attempt:</h2> <p>I manually set <code>DATABASE_URL</code> in my .env file changing the host. I replaced "ec2-184-73-162-34.compute-1.amazonaws.com:5432" by "localhost:5000". <code>$ deactivate</code> and then <code>$ workon hellodjango_venv</code> again. </p> <p><code>DATABASE_URL=postgres://dbuser:dbpassword@localhost:5000/dbname</code></p> <p>But, when I run <code>$ foreman start</code> command: </p> <pre><code>(hellodjango_venv)mac-pol:hellodjango_rep oubiga$ foreman start 17:38:41 web.1 | started with pid 687 17:38:41 web.1 | 2013-09-11 17:38:41 [687] [INFO] Starting gunicorn 18.0 17:38:41 web.1 | 2013-09-11 17:38:41 [687] [INFO] Listening at: http://0.0.0.0:5000 (687) 17:38:41 web.1 | 2013-09-11 17:38:41 [687] [INFO] Using worker: sync 17:38:41 web.1 | 2013-09-11 17:38:41 [690] [INFO] Booting worker with pid: 690 </code></pre> <p>and tried to open my app in the browser <code>http://0.0.0.0:5000</code>:</p> <pre><code>17:38:46 web.1 | 2013-09-11 10:38:46 [690] [ERROR] Error handling request 17:38:46 web.1 | Traceback (most recent call last): 17:38:46 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 131, in handle_request 17:38:46 web.1 | respiter = self.wsgi(environ, resp.start_response) 17:38:46 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/dj_static.py", line 59, in __call__ 17:38:46 web.1 | return self.application(environ, start_response) 17:38:46 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 236, in __call__ 17:38:46 web.1 | self.load_middleware() 17:38:46 web.1 | File "/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 53, in load_middleware 17:38:46 web.1 | raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e)) 17:38:46 web.1 | ImproperlyConfigured: Error importing middleware django.contrib.auth.middleware: "dlopen(/Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/psycopg2/_psycopg.so, 2): Library not loaded: @loader_path/../lib/libssl.1.0.0.dylib 17:38:46 web.1 | Referenced from: /Users/oubiga/Envs/hellodjango_venv/lib/python2.7/site-packages/psycopg2/_psycopg.so 17:38:46 web.1 | Reason: image not found" </code></pre> <p>This time, <code>dj_database_url.config()</code> returned: </p> <pre><code>{ 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'dbname', 'HOST': 'localhost', 'USER': 'dbuser', 'PASSWORD': 'dbpassword', 'PORT': 5000 } </code></pre> <h2>As a Third Attempt:</h2> <p>I installed autoenv <code>mac-pol:~ oubiga$ pip install autoenv</code><br> From this <a href="https://github.com/kennethreitz/autoenv/wiki/Cookbook" rel="nofollow">Cookbook</a> Kenneth Reitz wrote, I put:</p> <pre><code>use_env() { typeset venv venv="$1" if [[ "${VIRTUAL_ENV:t}" != "$venv" ]]; then if workon | grep -q "$venv"; then workon "$venv" else echo -n "Create virtualenv $venv now? (Yn) " read answer if [[ "$answer" == "Y" ]]; then mkvirtualenv "$venv" fi fi fi } </code></pre> <p>in my .bashrc file.</p> <p>I run <code>$ foreman start</code> command:</p> <pre><code>(hellodjango_venv)mac-pol:hellodjango_rep oubiga$ foreman start 18:11:57 web.1 | started with pid 1104 18:11:57 web.1 | 2013-09-11 18:11:57 [1104] [INFO] Starting gunicorn 18.0 18:11:57 web.1 | 2013-09-11 18:11:57 [1104] [INFO] Listening at: http://0.0.0.0:5000 (1104) 18:11:57 web.1 | 2013-09-11 18:11:57 [1104] [INFO] Using worker: sync 18:11:57 web.1 | 2013-09-11 18:11:57 [1107] [INFO] Booting worker with pid: 1107 </code></pre> <p>and tried to open my app in the browser <code>http://0.0.0.0:5000</code>: It worked!</p> <pre><code>^CSIGINT received 18:12:06 system | sending SIGTERM to all processes 18:12:06 web.1 | 2013-09-11 11:12:06 [1107] [INFO] Worker exiting (pid: 1107) SIGTERM received 18:12:06 web.1 | 2013-09-11 18:12:06 [1104] [INFO] Handling signal: int 18:12:06 web.1 | 2013-09-11 18:12:06 [1104] [INFO] Shutting down: Master 18:12:06 web.1 | exited with code 0 </code></pre> <p><strong>But, <code>dj_database_url.config()</code> again returns an empty dictionary.</strong></p> <h2>As a Final Attempt:</h2> <p>I was curious about the <code>python manage.py runserver</code> command and I checked it out.</p> <pre><code>(hellodjango_venv)mac-pol:hellodjango_rep oubiga$ foreman run python hellodjango/manage.py runserver Validating models... 0 errors found September 11, 2013 - 18:42:37 Django version 1.5.2, using settings 'hellodjango.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C. </code></pre> <p>and tried to open my app in the browser <code>http://127.0.0.1:8000/</code>: It didn't work!</p> <p>An <code>ImportError: No module named hellodjango.urls</code> was raised.</p> <p>I replaced <code>ROOT_URLCONF = 'hellodjango.hellodjango.urls'</code> in my settings.py file by <code>ROOT_URLCONF = 'hellodjango.urls'</code> and it finally worked.</p> <p><strong>As expected, <code>dj_database_url.config()</code> returned an empty dictionary.</strong></p> <h2>So:</h2> <p>Now, I feel a little overwhelmed. I'm afraid I'm misunderstanding some core concept here.</p> <ul> <li><strong>What's the point to use gunicorn instead of the Django development server?</strong></li> <li><strong>Why does <code>dj_database_url.config()</code> sometimes return a fully populated dictionary, and sometimes an empty one?</strong></li> <li><strong>Can I manually set environment variables in the .env file? Do I need to install tools like autoenv, heroku-config...?</strong></li> </ul> <p>Thank you in advance.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload