├── __init__.py ├── conf ├── __init__.py ├── base │ ├── __init__.py │ └── settings.py ├── dev │ ├── __init__.py │ ├── hosts.py │ └── settings.py ├── local │ └── settings.py └── prod │ └── settings.py ├── lib ├── __init__.py └── fabric_helpers.py ├── .gitignore ├── apps └── main │ ├── __init__.py │ ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── link_admin_media.py │ │ └── new_secret.py │ ├── views.py │ ├── models.py │ ├── tests.py │ └── urls.py ├── settings.py ├── db └── .gitignore ├── media └── .gitignore ├── static └── manual │ ├── images │ └── apatosaurus.png │ └── css │ └── styles.css ├── requirements.txt ├── fabfile.py ├── templates ├── base.html └── index.html ├── manage.py └── README.markdown /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conf/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /apps/main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conf/base/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /conf/dev/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/main/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | conf/local/settings.py -------------------------------------------------------------------------------- /db/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /media/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /apps/main/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/main/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /apps/main/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /static/manual/images/apatosaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordanorelli/Django-Skeleton/HEAD/static/manual/images/apatosaurus.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.3.1 2 | Fabric==1.2.2 3 | South==0.7.3 4 | django-debug-toolbar==0.8.5 5 | ipython==0.11 6 | paramiko==1.7.7.1 7 | pycrypto==2.3 8 | wsgiref==0.1.2 9 | -------------------------------------------------------------------------------- /fabfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import string 3 | from fabric.api import local, run, env, sudo, cd 4 | from lib.fabric_helpers import * 5 | 6 | def run_tests(): 7 | """ Runs the Django test suite as is. """ 8 | local("./manage.py test") 9 | 10 | def uname(): 11 | """ Prints information about the host. """ 12 | run("uname -a") 13 | -------------------------------------------------------------------------------- /conf/dev/hosts.py: -------------------------------------------------------------------------------- 1 | hosts = ( 2 | # { 3 | # 'user': 'stanley', # system username on the target host. 4 | # # Defaults to your local username. 5 | # 'hostname': '123.123.123.123', # a hostname 6 | # 'port': 666, # ssh port (you can leave this out for 22) 7 | # 'key_filename': '', # path to a private key, if necessary 8 | # }, 9 | ) 10 | -------------------------------------------------------------------------------- /apps/main/management/commands/link_admin_media.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | from django.conf import settings 3 | from django.contrib import admin 4 | import os 5 | 6 | class Command(BaseCommand): 7 | def handle(self, *args, **kwargs): 8 | admin_media_path = os.path.join(os.path.dirname(admin.__file__), 9 | 'media') 10 | print "Admin media: " + admin_media_path 11 | -------------------------------------------------------------------------------- /apps/main/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |Of course, you haven't actually done any work yet. Here's what to do next:
11 |DATABASES setting in django_skel/settings.py.15 |
21 | You can find this file in templates/index.html. You are seeing this because you have configured a route to that template, presumably in a file called "urls.py". Check settings.py for a ROOT_URLCONF setting, which should point you to where your URLConf was defined. 22 |
23 |
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/lib/fabric_helpers.py:
--------------------------------------------------------------------------------
1 | import os
2 | import string
3 | from fabric.api import env
4 |
5 | _conf_root = os.path.join(
6 | os.path.dirname(
7 | os.path.dirname(os.path.abspath(__file__))), 'conf')
8 |
9 | _conf_dirs = [x for x in os.listdir(_conf_root)
10 | if os.path.isdir(os.path.join(_conf_root, x))
11 | and x != 'base']
12 |
13 | def _mk_sethost_fn(host_dicts):
14 | def inner():
15 | """ (Autogenerated host connection function.) """
16 | hoststrings = []
17 | if env.key_filename == None: env.key_filename = []
18 | for host in host_dicts:
19 | hostname = host.get('hostname', '')
20 | user = host.get('user', '')
21 | port = host.get('port', '')
22 | hoststring = '%s%s%s' % (user and user + '@',
23 | hostname,
24 | port and ':' + str(port),
25 | )
26 | hoststrings.append(hoststring)
27 | key_filename = host.get('key_filename')
28 | if key_filename:
29 | env.key_filename.append(key_filename)
30 | env.hosts = hoststrings
31 | return inner
32 |
33 | for x in _conf_dirs:
34 | try:
35 | module_name = string.join(('conf', x, 'hosts'), '.')
36 | module = __import__(module_name, fromlist=['hosts',])
37 | locals()[x] = _mk_sethost_fn(module.hosts)
38 | except ImportError:
39 | pass
40 |
--------------------------------------------------------------------------------
/conf/base/settings.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 |
4 | # this is dumb
5 | PROJECT_ROOT = os.path.dirname(
6 | os.path.dirname(
7 | os.path.dirname(
8 | os.path.abspath(__file__))))
9 |
10 | sys.path.append(os.path.join(PROJECT_ROOT, 'apps'))
11 |
12 | # Local time zone for this installation. Choices can be found here:
13 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
14 | # although not all choices may be available on all operating systems.
15 | # On Unix systems, a value of None will cause Django to use the same
16 | # timezone as the operating system.
17 | # If running in a Windows environment this must be set to the same as your
18 | # system time zone.
19 | TIME_ZONE = 'America/New_York'
20 |
21 | # Language code for this installation. All choices can be found here:
22 | # http://www.i18nguy.com/unicode/language-identifiers.html
23 | LANGUAGE_CODE = 'en-us'
24 |
25 | SITE_ID = 1
26 |
27 | # If you set this to False, Django will make some optimizations so as not
28 | # to load the internationalization machinery.
29 | USE_I18N = True
30 |
31 | # If you set this to False, Django will not format dates, numbers and
32 | # calendars according to the current locale
33 | USE_L10N = True
34 |
35 | # Absolute filesystem path to the directory that will hold user-uploaded files.
36 | # Example: "/home/media/media.lawrence.com/media/"
37 | MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media')
38 |
39 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a
40 | # trailing slash.
41 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
42 | MEDIA_URL = '/media/'
43 |
44 | # Absolute path to the directory static files should be collected to.
45 | # Don't put anything in this directory yourself; store your static files
46 | # in apps' "static/" subdirectories and in STATICFILES_DIRS.
47 | # Example: "/home/media/media.lawrence.com/static/"
48 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static', 'auto')
49 |
50 | # URL prefix for static files.
51 | # Example: "http://media.lawrence.com/static/"
52 | STATIC_URL = '/static/'
53 |
54 | # URL prefix for admin static files -- CSS, JavaScript and images.
55 | # Make sure to use a trailing slash.
56 | # Examples: "http://foo.com/static/admin/", "/static/admin/".
57 | ADMIN_MEDIA_PREFIX = '/static/admin/'
58 |
59 | # Additional locations of static files
60 | STATICFILES_DIRS = (
61 | os.path.join(PROJECT_ROOT, 'static', 'manual'),
62 | # Put strings here, like "/home/html/static" or "C:/www/django/static".
63 | # Always use forward slashes, even on Windows.
64 | # Don't forget to use absolute paths, not relative paths.
65 | )
66 |
67 | # List of finder classes that know how to find static files in
68 | # various locations.
69 | STATICFILES_FINDERS = (
70 | 'django.contrib.staticfiles.finders.FileSystemFinder',
71 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
72 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
73 | )
74 |
75 | # Make this unique, and don't share it with anybody.
76 | SECRET_KEY = 'x^nvm7ezt%g1zxdxii7i32r5mc7srm-0!de30b5kz*zsic$lx%'
77 |
78 | # List of callables that know how to import templates from various sources.
79 | TEMPLATE_LOADERS = (
80 | 'django.template.loaders.filesystem.Loader',
81 | 'django.template.loaders.app_directories.Loader',
82 | # 'django.template.loaders.eggs.Loader',
83 | )
84 |
85 | MIDDLEWARE_CLASSES = (
86 | 'django.middleware.common.CommonMiddleware',
87 | 'django.contrib.sessions.middleware.SessionMiddleware',
88 | 'django.middleware.csrf.CsrfViewMiddleware',
89 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
90 | 'django.contrib.messages.middleware.MessageMiddleware',
91 | )
92 |
93 | ROOT_URLCONF = 'main.urls'
94 |
95 | TEMPLATE_DIRS = (
96 | os.path.join(PROJECT_ROOT, 'templates'),
97 | )
98 |
99 | INSTALLED_APPS = (
100 | 'django.contrib.auth',
101 | 'django.contrib.contenttypes',
102 | 'django.contrib.sessions',
103 | 'django.contrib.sites',
104 | 'django.contrib.messages',
105 | 'django.contrib.staticfiles',
106 | 'django.contrib.admin',
107 | # Uncomment the next line to enable admin documentation:
108 | # 'django.contrib.admindocs',
109 |
110 | 'south',
111 |
112 | 'main',
113 | )
114 |
115 | # A sample logging configuration. The only tangible logging
116 | # performed by this configuration is to send an email to
117 | # the site admins on every HTTP 500 error.
118 | # See http://docs.djangoproject.com/en/dev/topics/logging for
119 | # more details on how to customize your logging configuration.
120 | LOGGING = {
121 | 'version': 1,
122 | 'disable_existing_loggers': False,
123 | 'handlers': {
124 | 'mail_admins': {
125 | 'level': 'ERROR',
126 | 'class': 'django.utils.log.AdminEmailHandler'
127 | }
128 | },
129 | 'loggers': {
130 | 'django.request': {
131 | 'handlers': ['mail_admins'],
132 | 'level': 'ERROR',
133 | 'propagate': True,
134 | },
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | This is my personal Django skeleton project. It's not necessarily going to look exactly how you might lay out your own Django projects, it's just how I lay out mine.
2 |
3 | First, I recommend using [pip](http://pypi.python.org/pypi/pip) and [virtualenv](http://pypi.python.org/pypi/virtualenv) to manage your dependencies. It may seem like a headache at first, but it's honestly less of a headache than installing things globally since your OS provider may put packages in the global space that conflict with packages you wish to use in your project space. I have included a `requirements.txt` file that details all of the project requirements, which can be installed automatically in pip using `pip install -r requirements.txt`. pip won't cache downloaded packages automatically, but you can specify a local cache directory by exporting a `PIP_DOWNLOAD_CACHE` environment variable. I strongly recommend adding `export PIP_DOWNLOAD_CACHE=$HOME/.pip_cache` or something similar to your `.bashrc` to speed up the creation of similar virtual environments.
4 |
5 | I am also a fan of [virtualenvwrapper](http://pypi.python.org/pypi/virtualenvwrapper) for centralizing my virtual environments and project files. It's a big help, especially since you can add hooks to it. In a nutshell it allows you to specify two directories: one for storing virtual environments, the other for storing projects. When you say `mkvirtualenv` it creates your environment in your preconfigured environment directory. You can then say `lsvirtualenvs` to get a list of environments, `rmvirtualenv` to remove an environment by name, and `workon` to activate an environment by name instead of having to source the activation script manually or create your own alias every time you make a new environment (which is what I was doing in the past). The really useful thing is that it includes a `mkproject` function that will make a virtual environment and a project directory, that way when you say `workon my_project` it will activate the environment and navigate there at the same time. It has bash completion as well, so you can type workon at your command line and hit tab a few times to see which projects are on your system and what their names are. So cool! It also has hooks that will let you run arbitrary code when activating or deactivating an environment without having to modify the `bin/activate` script in your environment itself. Of course, with the proper shebang your hook scripts can be written in Python instead of bash/zsh/whatever.
6 |
7 | The way I have my project configured is a bit unconventional, and I don't think it will work out of box on Windows machines (sorry). Essentially what I do is create a `conf` directory at the root of the project. Within this directory there are subdirectories that each represent a host or group of hosts. The `settings.py` in particular uses `conf/base/settings.py` to define the settings that are common to all hosts, which can then (actually must) be extended. A basic localhost setup is illustrated in `conf/local/settings.py` using sqlite, which I find good enough for localhost development and quickly messing around.
8 |
9 | I put all of my project settings into the repository so that every host's settings are versioned, and then manage which settings file is active using a [symbolic link](http://en.wikipedia.org/wiki/Symbolic_link). The repository starts out with a symlink to `conf/local/settings.py`, which gives you baseline settings that will work out of box.
10 |
11 | The "welcome" page has been moved into the project itself so that the way Django routes URLs is more apparent. It's a bit weird that the default Django project has a welcome page that comes from a completely different area on your system. Putting the welcome page into the project makes Django's request-handling process a bit more transparent for first-timers.
12 |
13 | One thing that I haven't done is linked in the admin media yet, but that's because that's a per-host thing. For localhost development I just use the Django development server, which will serve up the admin media directly, and for servers I symlink in the admin media.
14 |
15 | Apps are put in their own `apps` directory, just to make things tidy, and the base settings file adds this directory to the python path at runtime. You can see in `conf/base/settings.py` how the `main` app is referenced.
16 |
17 | There's an additional `manage.py` command in `apps/main/management/commands/new_secret.py` that serves to illustrate how you can write your own `manage.py` commands and, more importantly, to regenerate the `SECRET_KEY` found in your `conf/base/settings.py` file. **Be sure to run this command to generate a unique SECRET\_KEY**. You can do so by typing `./manage.py new_secret` at the project root.
18 |
19 | I've also included a `fabfile.py`, which is used to create tasks for [Fabric](http://docs.fabfile.org/en/1.2.2/index.html), a library for managing SSH deployment. I don't generally condone using the `from modulename import *` syntax since it makes it very non-obvious where things are coming from, but it made it possible to dynamically create connection functions at runtime, which are generated based on host configurations. There's a sample configuration included in `conf/dev/hosts.py` that would show how you would, in theory, add a remote host configuration for a server called `dev`. If you fill that in with some real host details you'll be able to `fab dev uname` to use the included `uname` task. All it does it print out some info about the system, but it's enough to illustrate how to create Fabric tasks and test your host configuration. Unfortunately Fabric does not currently read your settings in `~/.ssh/config`, so you may have to repeat them.
20 |
21 | I personally add a little chunk of code to my `.bashrc` to create a `mkdjango` function, which will create a virtual environment of the same name, create a project directory, clone this skeleton into that directory, regenerate the SECRET\_KEY, remove this README, install all of the requirements to the virtualenv via pip, run `syncdb` to create a developmentdatabase, and fire up the application, all in one command. Requires pip, virtualenv, and virtualenvwrapper to work properly. Here is my `mkdjango` function:
22 |
23 | mkdjango () {
24 | mkproject --no-site-packages --prompt=$1: $1 &&
25 | git init &&
26 | git pull git://github.com/jordanorelli/Django-Skeleton.git master &&
27 | rm README.markdown &&
28 | pip install -r requirements.txt &&
29 | ./manage.py new_secret &&
30 | ./manage.py syncdb &&
31 | ./manage.py runserver
32 | }
33 |
34 | With that in my `~/.bashrc`, all I have to say is `mkdjango some_project_name` and I'm ready to rock.
35 |
--------------------------------------------------------------------------------