The last couple of days have been relatively productive. Thanks to GitHub's excellent API documentation, I've got to the stage where I can reliably authenticate a user with my application via Oauth, and use their key to pull basic user info such as their private email address and GitHub ID. In this post I'll talk briefly about the setup of the application and some of the design choices I've made so far.
I've written the server element of the project using Django 1.10. I went with Django mainly because of my familiarity with the ecosystem and the ease of setting up REST endpoints with Django REST Framework (DRF), which is definitely something I'll want to do further down the line.
Layout
The project layout is slightly different from the standard created by Django's startproject
command. For simplicity's sake, I prefer to put settings.py
, wsgi.py
etc in the root directory of the Django application, rather than having them in a separate, named folder. As a result, my tree looks like this:
.
├── alexandria_server
│ ├── permissions
│ │ ├── migrations
│ │ │ └── 0001_initial.py
│ │ ├── models.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── docker-compose.yml
├── Dockerfile
├── manage.py
└── requirements.txt
All of the configuration and deployment code lives at the project root, along with manage.py
. The application root (alexandria_server
) contains settings, base URL configuration and WSGI info, as well as any app directories.
Configuration
It was an interesting experience trying to see how few of Django's built-in modules were necessary for it to work. I've built Django projects before without many of the contrib
apps, but I think this was the first time that I'd completely removed the auth
app. Since the only login will be through GitHub initially, the user model in the application can be very basic and thus doesn't require the password hashing and other helpful features that Django auth provides.
Here's my extremely minimal settings.py
file currently. Initially, DRF was pretty unhappy with my removal of the auth
app, as it tries to assign an AnonymousUser
object to the request on an unauthenticated call. However, the UNAUTHENTICATED_USER
setting can be changed to another object, or None
, which circumvents this issue.
"""
Django settings for alexandria_server project.
Generated by 'django-admin startproject' using Django 1.10.
"""
import os
import dj_database_url
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
SECRET_KEY = os.environ.get('SECRET_KEY')
if os.environ.get('DEBUG'):
DEBUG = True
else:
DEBUG = False
GITHUB_CLIENT_ID = os.environ.get('GITHUB_CLIENT_ID')
GITHUB_CLIENT_SECRET = os.environ.get('GITHUB_CLIENT_SECRET')
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
# Core
'django.contrib.contenttypes',
'django.contrib.messages',
# Third-party
'rest_framework',
# Internal
'alexandria_server.permissions',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'alexandria_server.urls'
WSGI_APPLICATION = 'alexandria_server.wsgi.application'
# Database
DATABASES = {
'default': dj_database_url.parse(os.environ.get('DATABASE_URL')),
}
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# DRF
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [],
'DEFAULT_PERMISSION_CLASSES': [],
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
'UNAUTHENTICATED_USER': None
}
Only a couple of interesting things worth pointing out here. Firstly, as you can see, most of the default Django apps are gone. I'm not entirely sure if I need contenttypes
or messages
either, so those may disappear.
I'm using Kenneth Reitz's dj-database-url
to easily populate the configuration for the default database. I like this approach because it makes it trivially easy to set your DB locally, or in the cloud, simply by changing the DATABASE_URL
environment variable.
Finally, I'm going for the classic 12 factor app approach to settings, so all variables and secrets are pulled in from environment variables. I prefer this tactic to the alternative of having a settings.py
file per environment, perhaps not checked in to source control. I feel like it gives more flexibility but also more consistency between environments.
Database
My go-to database choice for Django has always been Postgres. They play together very nicely, and Postgres has a host of awesome features which are fully supported in Django that no other database has. It's also extremely easy, thanks to Docker, to run a Postgres database locally in a container. Here's the setup in my docker-compose.yml
:
db:
image: postgres:9.5
environment:
- POSTGRES_PASSWORD=some_password
- POSTGRES_USER=some_user
It's amazing to be able to spin up a local database with only 5 lines of configuration code.
In a separate post, I'll talk about the Oauth flow in more detail. If you have any questions or comments about project layout, or configuring a basic Django setup, let me know in the comments.