Guguweb - herokuhttps://www.guguweb.com/2022-01-07T15:56:58+01:00Freelance developer and sysadminHow to migrate your existing Django project to Heroku2016-05-18T14:15:45+00:002022-01-07T15:56:58+01:00Augusto Destrerotag:www.guguweb.com,2016-05-18:/2016/05/18/how-to-migrate-your-existing-django-project-to-heroku/<hr>
<p>Recently I had some fun with <a href="https://www.heroku.com/" target="_blank" rel="noopener noreferrer">Heroku</a>, the well known PaaS provider. I had a small personal Django project I use for invoicing that I ran locally with ./manage.py runserver when needed. That was a perfect candidate for the <a href="https://www.heroku.com/pricing" target="_blank" rel="noopener noreferrer">Heroku free plan</a> because I need to access the app …</p><hr>
<p>Recently I had some fun with <a href="https://www.heroku.com/" target="_blank" rel="noopener noreferrer">Heroku</a>, the well known PaaS provider. I had a small personal Django project I use for invoicing that I ran locally with ./manage.py runserver when needed. That was a perfect candidate for the <a href="https://www.heroku.com/pricing" target="_blank" rel="noopener noreferrer">Heroku free plan</a> because I need to access the app only occasionally.</p>
<p>In this tutorial I assume you have a basic knowledge of what Heroku is, and that you already know how to create and deploy a Python project on Heroku. In case you miss some basic information you can refer to the good <a href="https://devcenter.heroku.com/articles/getting-started-with-python" target="_blank" rel="noopener noreferrer">Getting started tutorial on Heroku with Python</a>.</p>
<div class="toc"><span class="toctitle">Table of Contents</span><ul>
<li><a href="#how-to-structure-your-django-project-for-heroku">How to structure your Django project for Heroku</a></li>
<li><a href="#how-to-configure-your-django-project-for-heroku">How to configure your Django project for Heroku</a></li>
<li><a href="#create-an-heroku-application-for-your-django-project">Create an Heroku application for your Django project</a></li>
<li><a href="#migrating-data">Migrating data</a></li>
<li><a href="#media-files-on-aws-s3">Media files on AWS S3</a></li>
</ul>
</div>
<h2 id="how-to-structure-your-django-project-for-heroku">How to structure your Django project for Heroku</h2>
<p>Here I focus on my use case, which was to migrate an existing Django project on Heroku platform. My existing Django project was structured in accordance to the best practices I read in the wonderful <a href="http://twoscoopspress.com/products/two-scoops-of-django-1-8" target="_blank" rel="noopener noreferrer">Two Scoops of Django</a> book, so my project structure was similar to this:</p>
<div class="highlight"><pre><span></span><code>django/
├── project
│ ├── __init__.py
│ ├── settings
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── local.py
│ │ └── production.py
│ ├── urls.py
│ ├── wsgi.py
├── app
│ ├── __init__.py
│ ├── admin.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ ├── views.py
└── manage.py
requirements
├── base.txt
├── local.txt
└── production.txt
</code></pre></div>
<p>See how I have different settings for each deploy environment: one for local development, one for production. All common settings go in base.py.</p>
<p>See also how I have a different requirements file for each environment, for example I have django-debug-toolbar in local.txt, while I have psycopg2 in production.txt. All common requirements are in base.txt.</p>
<p>For Heroku I created a file <em>heroku.txt</em> under <em>requirements/</em> directory with the following content:</p>
<div class="highlight"><pre><span></span><code>-r base.txt
dj-database-url==0.3.0
psycopg2==2.6.1
whitenoise==2.0.2
django-storages==1.4.1
boto==2.38.0
</code></pre></div>
<h2 id="how-to-configure-your-django-project-for-heroku">How to configure your Django project for Heroku</h2>
<p>On Heroku I will use Postgres as database backend (which is free , and <a href="https://github.com/kennethreitz/dj-database-url" target="_blank" rel="noopener noreferrer">dj-database-url</a> to configure the database parameters using an environment variable, which is already set by Heroku.</p>
<p>I’ll also use <a href="http://whitenoise.evans.io/en/stable/django.html" target="_blank" rel="noopener noreferrer">whitenoise</a> to handle static files directly from django and <a href="https://github.com/jschneier/django-storages" target="_blank" rel="noopener noreferrer">django-storages</a> to handle media files on Amazon S3.</p>
<p>The first thing you have to do to tell Heroku you are deploying a python project is to put a <code>requirements.txt</code> file in the root of your git project. You can create a file with this content to include the requirements specific to Heroku:</p>
<div class="highlight"><pre><span></span><code>-r requirements/heroku.txt
</code></pre></div>
<p>Then you have to do some modification to your settings, for deploy on Heroku platform:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Parse database configuration from $DATABASE_URL</span>
<span class="kn">import</span> <span class="nn">dj_database_url</span>
<span class="kn">from</span> <span class="nn">os</span> <span class="kn">import</span> <span class="n">environ</span>
<span class="kn">from</span> <span class="nn">.base</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">ALLOWED_HOSTS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'yourappname.herokuapp.com'</span><span class="p">]</span>
<span class="n">DATABASES</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'default'</span><span class="p">:</span> <span class="n">dj_database_url</span><span class="o">.</span><span class="n">config</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">WSGI_APPLICATION</span> <span class="o">=</span> <span class="s1">'yourproject.wsgi_heroku.application'</span>
<span class="n">STATICFILES_STORAGE</span> <span class="o">=</span> <span class="s1">'whitenoise.django.GzipManifestStaticFilesStorage'</span>
<span class="n">STATIC_ROOT</span> <span class="o">=</span> <span class="n">root</span><span class="p">(</span><span class="s1">'static_root'</span><span class="p">)</span>
<span class="n">INSTALLED_APPS</span> <span class="o">+=</span> <span class="p">(</span><span class="s1">'storages'</span><span class="p">,)</span>
<span class="n">DEFAULT_FILE_STORAGE</span> <span class="o">=</span> <span class="s1">'storages.backends.s3boto.S3BotoStorage'</span>
<span class="n">AWS_ACCESS_KEY_ID</span> <span class="o">=</span> <span class="n">environ</span><span class="p">[</span><span class="s1">'AWS_ACCESS_KEY_ID'</span><span class="p">]</span>
<span class="n">AWS_SECRET_ACCESS_KEY</span> <span class="o">=</span> <span class="n">environ</span><span class="p">[</span><span class="s1">'AWS_SECRET_ACCESS_KEY'</span><span class="p">]</span>
<span class="n">AWS_STORAGE_BUCKET_NAME</span> <span class="o">=</span> <span class="s1">'yourbucketname'</span>
</code></pre></div>
<p>Then you have to edit your wsgi.py file to make use of whitenoise for static files, the file whould look something like this. Save it in a different wsgi_heroku.py file:</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">django.core.wsgi</span> <span class="kn">import</span> <span class="n">get_wsgi_application</span>
<span class="kn">from</span> <span class="nn">whitenoise.django</span> <span class="kn">import</span> <span class="n">DjangoWhiteNoise</span>
<span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">"DJANGO_SETTINGS_MODULE"</span><span class="p">,</span> <span class="s2">"yourproject.settings.heroku"</span><span class="p">)</span>
<span class="n">application</span> <span class="o">=</span> <span class="n">get_wsgi_application</span><span class="p">()</span>
<span class="n">application</span> <span class="o">=</span> <span class="n">DjangoWhiteNoise</span><span class="p">(</span><span class="n">application</span><span class="p">)</span>
</code></pre></div>
<h2 id="create-an-heroku-application-for-your-django-project">Create an Heroku application for your Django project</h2>
<p>Now you can create you Heroku application:</p>
<div class="highlight"><pre><span></span><code><span class="n">heroku</span> <span class="n">create</span> <span class="o">--</span><span class="n">region</span> <span class="n">eu</span> <span class="n">yourappname</span>
</code></pre></div>
<p>In the root directory of your project you should have a Procfile, used by Heroku to know how to launch the web worker, in our case the Procfile should contain just one line like the following:</p>
<div class="highlight"><pre><span></span><code><span class="n">web</span><span class="o">:</span> <span class="n">gunicorn</span> <span class="o">--</span><span class="n">pythonpath</span> <span class="n">django</span> <span class="n">yourproject</span><span class="o">.</span><span class="na">wsgi_heroku</span> <span class="o">--</span><span class="n">log</span><span class="o">-</span><span class="n">file</span> <span class="o">-</span>
</code></pre></div>
<p>Now push the code on Heroku:</p>
<div class="highlight"><pre><span></span><code>git push heroku master
</code></pre></div>
<p>Then you can create the database schema with the usual migrate command, run on the Heroku instance:</p>
<div class="highlight"><pre><span></span><code>heroku run python django/manage.py migrate
</code></pre></div>
<h2 id="migrating-data">Migrating data</h2>
<p>If you have some data to migrate you can use a couple of command from core Django</p>
<div class="highlight"><pre><span></span><code>./manage.py dumpdata yourapp > yourapp/fixtures/app_data.json
</code></pre></div>
<p>Then you can import the data on Heroku instance:</p>
<div class="highlight"><pre><span></span><code><span class="n">heroku</span> <span class="n">run</span> <span class="n">python</span> <span class="n">django</span><span class="o">/</span><span class="n">manage</span><span class="o">.</span><span class="n">py</span> <span class="n">loaddata</span> <span class="n">app_data</span>
</code></pre></div>
<h2 id="media-files-on-aws-s3">Media files on AWS S3</h2>
<p>If your application needs some user uploaded data, you can use Amazon S3 to store the files, by using django-storages app to configure the correct Storage for you media files.</p>
<p>The configuration of django-storages is out of scope for this tutorial, but you can find some <a href="https://www.caktusgroup.com/blog/2014/11/10/Using-Amazon-S3-to-store-your-Django-sites-static-and-media-files/" target="_blank" rel="noopener noreferrer">good tutorials</a> on the web.</p>