Skip to content

Commit

Permalink
Merge branch 'django1.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
senko committed Apr 7, 2012
2 parents 81fbbe3 + 9ab227a commit 7031f8f
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 95 deletions.
8 changes: 4 additions & 4 deletions .gitignore
@@ -1,5 +1,5 @@
*.pyc
settings/local.py
/static/
/media/
/test.db
/*/settings/local.py
/*/static/
/*/media/
/dev.db
125 changes: 106 additions & 19 deletions README.md
Expand Up @@ -4,16 +4,29 @@ DJ Skeletor is a skeleton Django project handy for bootstrapping new
empty projects.

The repository contains an empty, relocatable Django project with South,
Django Debug Toolbar and Sentry apps set, and with provisions for test and
production settings.
Django Debug Toolbar and Raven apps set, and with provisions for development,
test and production settings.


### Features

Comes with:

* South for database migrations
* Django Debug Toolbar for displaying extra information about your view excution
* Sentry client for logging exceptions (and custom logging)
* Fabric for easy deployment to remote servers
* Test-specific settings for running test with an in-memory SQLite database
* SQLite database configured in the default development settings

DJ Skeletor requires Django 1.4 or later. All the other requirements are
optional and can be disabled.


### Quick setup

# prepare the virtual environment
virtualenv --no-site-packages myenv
cd myenv
source bin/activate
mkvirtualenv --no-site-packages myenv

# get the skeleton project
git clone https://github.com/senko/dj-skeletor.git myproject
Expand All @@ -22,17 +35,18 @@ production settings.
# install requirements
pip install -r requirements.txt

# rename the project directory to reflect your project name
git mv skeletor myproject && git commit -m 'renamed project'

# activate dev environment
cd settings
ln -s dev.py local.py
cd ..
echo 'from .dev import *' > myproject/settings/local.py

# initialize the database
python manage.py syncdb
python manage.py migrate

# collect the static files needed by the apps
python manage.py collectstatic
python manage.py collectstatic --noinput

# run it!
python manage.py runserver
Expand All @@ -43,16 +57,23 @@ production settings.
Settings are handled in the *settings* module:

* settings.base - base/invariant project settings
* settings.dev - development/test environment
* settings.dev - development environment
* settings.prod - production environment
* settings.test - test environment
* settings.local - local environment

If you're storing your environment settings in the repository, the easiest
way to activate it on the server is to symlink settings/local.py to either
prod or dev settings module.

If you like to keep the local settings out of the git repository, use dev.py
or prod.py as a template and create your local.py as needed.
or prod.py as a template and create your local.py as needed. The quick
setup procedure and fabric setup tasks create a simple local.py that just
imports everything from the development environment.

Test settings are separate to make it easier to customize settings for use
when running unit tests. The default test settings use in-memory SQLite
database and turn off South migrations and Sentry logging.

### Django Debug Toolbar

Expand All @@ -62,13 +83,79 @@ the production environment.

### Database

A simple sqlite3 database is configured in the settings, so no additional
configuration is needed to start hacking right away. South is used for
schema migrations.
A simple sqlite3 database is configured in the development settings, so
no additional configuration is needed to start hacking right away. South
is used for schema migrations.

The database filename used is 'dev.db' in the project root directory. It is
explicitly ignored by git and fabric when rsyncing the local directory to server.


### Sentry / Raven

To use the Sentry client, you'll need a server to point it to. Installing
Sentry server is easy as:

# mkvirtualenv --no-site-packages sentry-env
# pip install sentry
# sentry init
# sentry start

You'll want to install Sentry into its own environment as it requires
Django 1.2 or 1.3 at the moment.

If you don't want to install Sentry yourself, you can use a hosted
version at http://getsentry.com/.

When you connect to your (or hosted) Sentry server and create a new project
there, you'll be given Sentry DSN which you need to put into settings/base.py
to activate Sentry logging.

### Fabric

A fabfile is provided with common tasks for rsyncing local directory to
the server for use while developing the project, and for deploying the
project using git clone/pull.

Useful commands:

* server - host to connect to (same as -H, but accepts only one argument)
* env - virtualenv name on the server, as used with virtualenvwrapper/workon
* project_path - full path to the project directory on the server
* rsync - use rsync to copy the local folder to the project directory on the server
* setup - set up the project instance on the server (clones the origin
repository, creates a virtual environment, initialises the database and
runs the tests)
* deploy - deploy a new version of project on the server using git pull
* collecstatic, syncdb, migrate, runserver - run manage.py command
* update - combines collecstatic, syncdb, migrate
* test - run manage.py test with the test settings enabled

For all the commands, run 'fab -l' or look at the source.

#### Examples:

Copy local directory to the server, update database and static files, and
run tests (only files changed from last copy are going to be copied):

fab server:my.server.com env:myenv project_path:/path/to/project rsync update test

Deploy a new instance of a project on a server ('myenv' will be newly created,
code will be cloned into /path/to/project):

fab server:my.server.com env:myenv project_path:/path/to/project \
setup:origin=http://github.com/senko/dj-skeletor

Deploy a new version of the project on the server (a new git tag will be
created for each deployment, so it's easy to roll-back if needed):

fab server:my.server.com env:myenv project_path:/path/to/project deploy

### Sentry
##### Customization

Sentry is used in the integrated setup, ie. as an app inside the Django
project. This makes things simpler when starting. When the project grows,
or if you have several apps you need to monitor, consider switching to
running Sentry in client/server mode.
Everyone has a slightly different workflow, so you'll probably want to
customize the default fabric tasks or combine them. You can either customize
fabfile.py and commit the changes to your repository, or you can create
local_fabfile.py, which will be loaded if it exists. The latter can be useful
if you have per-team-member fabric customizations you don't want to commit
to the repository.
161 changes: 161 additions & 0 deletions fabfile.py
@@ -0,0 +1,161 @@
# Common fabric tasks

import os.path
from fabric.api import *
from fabric.contrib.project import rsync_project

def _cd_project_root():
assert hasattr(env, 'project_path')
return cd(env.project_path)

def _activate():
assert hasattr(env, 'virtualenv')
if '/' in env.virtualenv:
return prefix('source ' + env.virtualenv + '/bin/activate')
else:
return prefix('source ~/.bash_profile 2>/dev/null || ' +
'source ~/.profile 2>/dev/null || true &&' +
'workon ' + env.virtualenv)

def _discover_project_name():
from os import listdir
from os.path import join, dirname, exists
project_name = None
local_root = dirname(__file__)
for subdir in listdir(local_root):
if exists(join(local_root, subdir, 'settings', 'test.py')):
project_name = subdir
break
return project_name

# Settings

def env(venv):
"""Virtual environment to use on the server"""
env.virtualenv = venv

def server(server):
"""Server to use"""
env.hosts = [server]

def path(path):
"""Project path on the server"""
env.project_path = path

# Base commands

def rsync():
"""Sync remote files to the server using rsync."""
assert hasattr(env, 'project_path')
local_dir = os.path.dirname(__file__) + '/'
rsync_project(remote_dir=env.project_path, local_dir=local_dir,
exclude=['media/', 'static/', '*.pyc', '.git/', 'dev.db'])

def manage(cmd):
"""Run Django management command on the server"""
with _activate():
with _cd_project_root():
run('python manage.py ' + cmd)

def git_pull(remote='origin'):
"""Pull newest version from the git repository"""
assert hasattr(env, 'project_path')
with _cd_project_root():
run('git pull ' + remote)

def git_clone(origin):
"""Create a new project instance by cloning the source repository"""
assert hasattr(env, 'project_path')
run('git clone %s %s' % (origin, env.project_path))

def git_tag_now(prefix):
"""Tag the current branch HEAD with a timestamp"""
import datetime
assert hasattr(env, 'project_path')
with _cd_project_root():
run('git tag %s-%s' % (prefix,
datetime.datetime.now().strftime('-%Y-%m-%d-%H-%M-%S')))

# High-level commands

def install_requirements():
"""Install required Python packages (from requirements.txt)"""
with _activate():
with _cd_project_root():
run('pip install -r requirements.txt')

def collectstatic():
"""Collect static files using collectstatic."""
manage('collectstatic --noinput')

def syncdb():
"""Execute initial syncdb"""
manage('syncdb')

def migrate():
"""Execute any pending South migrations on the server."""
manage('migrate')

def test():
"""Run Django tests"""
assert hasattr(env, 'project_path')
project_name = _discover_project_name()
if project_name:
manage('test --settings=%s.settings.test' % project_name)
else:
manage('test')

def update():
"""Do a complete project update.
This combines:
- installation of (newly) required Python packages via pip
- collect new static files
- upgrade locales,
- db sync / migrations.
"""
install_requirements()
collectstatic()
syncdb()
migrate()

def setup(origin):
"""Create an initial deployment from the source git repository.
This also sets up a sample settings/local.py which just pulls all
the settings from dev.py
"""
assert hasattr(env, 'project_path')
assert hasattr(env, 'virtualenv')
git_clone(origin)
git_tag_now('initial-deploy')
with prefix('source ~/.bash_profile 2>/dev/null || ' +
'source ~/.profile 2>/dev/null || true'):
run('mkvirtualenv --no-site-packages ' + env.virtualenv)
project_name = _discover_project_name()
if project_name:
fname = project_name + '/settings/local.py'
with _cd_project_root():
run('test -f %(fname)s || echo "from .dev import *" > %(fname)s' % {
'fname': fname
})
update()
test()

def deploy():
"""Deploy a new version of the app from the tracked git branch."""
assert hasattr(env, 'project_path')
assert hasattr(env, 'virtualenv')
git_pull()
git_tag_now('deploy')
update()
test()

def runserver(host='0.0.0.0', port='8000'):
"""Run a development server on host:port (default 0.0.0.0:8000)"""
manage('runserver %s:%s' % (host, port))

try:
from local_fabfile import *
except ImportError:
pass
33 changes: 22 additions & 11 deletions manage.py
@@ -1,14 +1,25 @@
#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
sys.exit(1)

import settings
from os import environ, listdir
from os.path import join, dirname, abspath, exists
import sys

if __name__ == "__main__":
execute_manager(settings)

# Try to discover project name and set the default settings module
# based on it. If discovery fails, DJANGO_SETTINGS_MODULE environment
# variable must be set.

root = dirname(abspath(__file__))
sys.path.append(root)
settings_module = None
for name in listdir(root):
full_name = join(root, name)
if exists(join(full_name, 'settings', '__init__.py')):
settings_module = name + '.settings'
break

if settings_module is not None:
environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)

from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

0 comments on commit 7031f8f

Please sign in to comment.