Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jay-johnson/f1a8352367abcfedb2079177681d47bf to your computer and use it in GitHub Desktop.
Save jay-johnson/f1a8352367abcfedb2079177681d47bf to your computer and use it in GitHub Desktop.
Set up a Django + ReactJS project with Webpack manager

Guide on how to create and set up your Django project with webpack, npm and ReactJS :)

Hopefully this will answer "How do I setup or start a Django project?" I was trying to set up my own website, and there was a lot to read, learn and digest! Therefore, I put this together which is a collection of all the guides/blog posts/articles that I found the most useful. At the end of this, you will have your barebones Django app configured and ready to start building :)

NOTE: This guide was built using Django 1.9.5, NodeJS 4+ with NPM 3+

1. Setting up your dev environment

If working on Mac OSX, follow this guide.

Requirements

  • PostgreSQL
  • virtualenv
  • virtualenvwrapper
  • git
  • NodeJS 4+ with NPM 3+

2. Create a Virtual Environment

It’s common practice to use a virtual environment for your Python projects in order to create self-contained development environments. The goal of virtualenv is to prevent different versions of libraries/packages from messing with each other. It’s like an isolated, soundproof room within your home where you can scream as loud as you want, about anything you want, and nobody else outside that room can hear it.

Use virtualenvwrapper to create a virtual environment for your project:

mkvirtualenv -p /usr/local/bin/python3 [project-name]

After running this command you should see (project-name) before your prompt, indicating that you’re running within the project-name virtualenv.

You should see something like this:

(project-name) MacBook-Air:~ michaelgonzalez$

3. Install Python packages

I start off every project with the same 7 Python packages:

pip install django-webpack-loader
pip install dj-database-url
pip install django
pip install gunicorn
pip install psycopg2
pip install python-decouple
pip install requests
pip install whitenoise

4. Create a directory for your Django project

This one is easy:

mkdir [project-name]
cd [project-name]

5. Setup Django project

Now lets automagically generate some code that establishes a Django project. Run the following command:

django-admin startproject [project-name] .

This should have created the following folder structure:

[project-name]/
    manage.py
    [project-name]/
        __init__.py
        settings.py
        urls.py
        wsgi.py

6. Create README.md file

This file will obviously be very simple to start, but it doesn't hurt to start describing your project :). You can fill in all the details as you develop! Make sure to visit this Markdown cheatsheet, which will allow you to create well designed and organized README files.

7. Create .gitignore file

This file will specify intentionally untracked files that Git should ignore. For more info, look here.

The contents of .gitignore:

venv
*.pyc
staticfiles
.env
env
node_modules
[project-name]/static/build
db.sqlite3
.DS_Store
__pycache__

8. Use git and GitHub for version control

I personally choose to use git for my software version control. I also use GitHub, which is a web-based Git repository hosting service. These tools help with deployment, working on teams, etc. This makes your life so much easier, trust me!

First things first, make sure to create a new repo at GitHub.com and DO NOT initialize with a README.md.

After you have created the repo, run these commands:

git init
git add .
git commit -m "first commit"
git remote add origin https://github.com/user-name/[project-name].git
git push -u origin master

9. Create .editorconfig file

EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. This file will initialize the coding style for this project.

I base my .editorconfig on PEP 0008 and AirBnB JavaScript Style Guide. Here are the contents of .editorconfig:

root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Set default charset for JavaScript and Python
[*.{js,py}]
charset = utf-8

# Python settings
[*.py]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

# JavaScript, JSX, CSS, SCSS, HTML settings
[*.{js,jsx,css,scss,html}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

# Markdown settings
[*.md]
trim_trailing_whitespace = false

10. Configure Python package requirements

Initialize the base Python package requirements for your Django project:

pip freeze > requirements.txt

Now create a requirements folder, so we can manage different required packages for different environments:

mkdir requirements
echo "r- base.txt" > requirements/production.txt
echo "r- base.txt" > requirements/local.txt
echo "r- base.txt" > requirements/testing.txt
cp requirements.txt requirements/base.txt

11. Reconfigure Django project settings

Lets transform the project settings into a package. This ensures we have the flexibility to have different settings for different environments.

1. Convert the settings.py into a package

Your Django project should now look like:

[project-name]/
    README.md
    manage.py
    config/
        __init__.py
        settings/
            __init__.py
            base.py #This was settings.py but was renamed to base.py
            local.py
            production.py
            testing.py
        urls.py
        wsgi.py
    requirements/
        base.txt
        local.txt
        production.txt
        testing.txt
    requirements.txt

2. Update DJANGO_SETTINGS_MODULE

The DJANGO_SETTINGS_MODULE environment needs to be changed in two places:

  • manage.py
  • config/wgsi.py

Lets update the manage.py file.

Before:

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "[project-name].settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

After:

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.base")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

Lets update the config/wgsi.py file.

Before:

"""
WSGI config for [project-name] project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "[project-name].settings")

application = get_wsgi_application()

After:

"""
WSGI config for [project-name] project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.base")

application = get_wsgi_application()

3. Update ROOT_URLCONF, BASE_DIR, WSGI_APPLICATION

Now that we have rearragned Django settings, we need to make some changes in the base settings to reflect this.

We will make a few changes to the config/settings/base.py file:

  1. BASE_DIR
  • Before:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  • After:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  1. ROOT_URLCONF
  • Before: ROOT_URLCONF = '[project-name].urls'
  • After: ROOT_URLCONF = 'config.urls'
  1. WSGI_APPLICATION
  • Before: WSGI_APPLICATION = '[project-name].wsgi.application'
  • After: WSGI_APPLICATION = 'config.wsgi.application'

4. Create APPS_DIR variable

We will make this change near the top of the config/settings.base.py file near the BASE_DIR variable:

Before:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

After:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
APPS_DIR = '{0}/apps'.format(BASE_DIR)

5. Adjust config/settings/base.py settings to take common project templates and static assests into account

In order for the project to know to look for common project templates, we need to adjust the TEMPLATE variable, specifically the DIRS key.

Before:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

After:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '{0}/templates'.format(APPS_DIR),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

In order for the project to know to look for common project staticfiles, we need to add the STATICFILES_DIRS, STATIC_ROOT, and WEBPACK_LOADER variables. I put these variables right under the STATIC_URL variable.

STATICFILES_DIRS:

STATICFILES_DIRS = [
    '{0}/static'.format(APPS_DIR),
]

STATIC_ROOT:

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

WEBPACK_LOADER:

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': 'bundles/',
        'STATS_FILE': os.path.join(BASE_DIR, '../webpack-stats.json')
    }
}

6. Adjust INSTALLED_APPS, so we can track which apps are DJango, internal, and external.

In the config/settings/base.py file, find the INSTALLED_APPS variable.

Before:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

After:

DJANGO_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

THIRD_PARTY_APPS = [
    'webpack_loader',
]

PROJECT_APPS = []

INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS

12. Reconfigure Django settings to use environment variables

The use of Decouple helps you to organize your settings so that you can change parameters without having to redeploy your app. We will be able to store project parameters in our .env file.

1. Add new packages to import

In the import section of config/settings/base.py add:

import dj_database_url
from decouple import config

2. Update SECRET_KEY to use .env

In config/settings/base.py we will update SECRET_KEY:

  • Before: SECRET_KEY = '3@@5e9=4ux8lbi-uwkb4bqa2a(7276spkiyl3&-w7v_p)iqd+%'
  • After: SECRET_KEY = config('SECRET_KEY')

3. Update DEBUG to use .env

In config/settings/base.py we will update DEBUG:

  • Before: DEBUG = True
  • After: DEBUG = config('DEBUG', cast=bool)

4. Update ALLOWED_HOSTS to use .env

In config/settings/base.py we will update ALLOWED_HOSTS:

  • Before: ALLOWED_HOSTS = []

  • After:

ALLOWED_HOSTS = config(
    'ALLOWED_HOSTS',
    cast=lambda v: [d for d in [s.strip() for s in v.split(' ')] if d],
    default='',
)

5. Update DATABASES to use .env

  • Before:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
  • After:
DATABASES = {
    'default': dj_database_url.parse(config('DATABASE_URL')),
}

6. Create .env file

MAKE SURE .env is in your .gitignore file!. You need to keep that file private!

The contents of .env:

SECRET_KEY=3@@5e9=4ux8lbi-uwkb4bqa2a(7276spkiyl3&-w7v_p)iqd+%
DEBUG=true
ALLOWED_HOSTS=localhost .project-url.com 127.0.0.1 0.0.0.0
DATABASE_URL=postgres://user:password@localhost:5432/database_name

13. Set up PostgreSQL database

Initiate PostgreSQL shell

psql

Create project database

CREATE DATABASE [project-name];

Create database user

CREATE USER user WITH PASSWORD 'password';
ALTER ROLE user SET client_encoding TO 'utf8';
ALTER ROLE user SET default_transaction_isolation TO 'read committed';
ALTER ROLE user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE [project-name] TO user;

14. Prepare your Django app to use npm, webpack and ReactJS

We will use NPM to manage frontend dependencies instead of managing them manually in one of the static files directories.

1. Setting up webpack

npm init
npm install --save-dev react webpack webpack-bundle-tracker babel-core babel babel-loader webpack-dev-server react-hot-loader

2. Create webpack config

mkdir -p apps/static/js
touch webpack.config.js
touch apps/static/js/index.js

Populate webpack.config.js with:

var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')


module.exports = {
  context: __dirname,
  entry: [
      'webpack-dev-server/client?http://localhost:3000',
      'webpack/hot/only-dev-server',
      './apps/static/js/index'
  ],

  output: {
      path: path.resolve('./apps/static/bundles/'),
      filename: '[name]-[hash].js',
      publicPath: 'http://localhost:3000/static/bundles/', // Tell django to use this URL to load packages and not use STATIC_URL + bundle_name
  },

  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(), // don't reload if there is an error
    new BundleTracker({filename: './webpack-stats.json'}),
  ],

  module: {
    loaders: [
      // we pass the output from babel loader to react-hot loader
      { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['react-hot', 'babel'], },
    ],
  },

  resolve: {
    modulesDirectories: ['node_modules', 'bower_components'],
    extensions: ['', '.js', '.jsx']
  }
}

5. Add some scripts to npm pacakage manager for easy dev

Open package.json and add the following:

  ...
  "scripts": {
  "build": "webpack --config webpack.config.js --progress --colors",
  "build-production": "webpack --config webpack.prod.config.js --progress --colors",
  "watch": "node server.js"
  }

4. Create server.js to run webpack-dev-server to both compile and serve our bundles.

Create a file server.js with:

var webpack = require('webpack')
var WebpackDevServer = require('webpack-dev-server')
var config = require('./webpack.config')

new WebpackDevServer(webpack(config), {
  publicPath: config.output.publicPath,
  hot: true,
  inline: true,
  historyApiFallback: true
}).listen(3000, '0.0.0.0', function (err, result) {
  if (err) {
    console.log(err)
  }

  console.log('Listening at 0.0.0.0:3000')
})

As you develop have a terminal running:

node server.js

Now as you develop, any changes made to the react components will reflect in the browser. No reload needed. Magic! right? Just run:

npm run watch

This will do continous builds as things change. The only exception is if you make any changes to the webpack configuration though.

15. Run migrations

Congrats! Your Django project is now set up. Lets run migrations and then verify everything is set up correctly.

1. Run makemigrations:

python manage.py makemigrations

This will output:

No changes detected

2. Run migrate:

python manage.py migrate

This will output:

Operations to perform:
  Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying sessions.0001_initial... OK

3. Run server

Now you are ready to run your application locally!

Run this command:

python manage.py runserver

This will output:

Performing system checks...

System check identified no issues (0 silenced).
April 16, 2016 - 13:21:27
Django version 1.9.5, using settings 'config.settings.base'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

In your web browser, open the followig URL: http://127.0.0.1:8000/.

You should see something like this:

alt text

Congrats Your Django up is set up and running correctly :)

Congrats! You have made it through this guide and have your Django + ReactJS project up and running :)

Follow this guide to set up your first Django app.

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment