diff --git a/.gitignore b/.gitignore index 3f36ae8c..003c6a05 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ *.pot *.pyc local_settings.py -repo_name \ No newline at end of file +repo_name +staticfiles +.DS_Store \ No newline at end of file diff --git a/README.rst b/README.rst index d2e4f3a9..fbe66d91 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ A cookiecutter_ template for Django. Features --------- -* Cutting edge: For Django 1.6 and other bleeding edge stuff +* Cutting edge: For Django 1.5 and other bleeding edge stuff * Twitter Bootstrap 3 * AngularJS * Settings management via django-configurations @@ -25,12 +25,6 @@ Constraints * PostgreSQL everywhere * Environment variables for configuration (This won't work with Apache/mod_wsgi). -Caution: Bleeding Edge Requirements ------------------------------------- - -The cookiecutter-django project is bleeding edge in that it uses the Django 1.6c1 release candidate. - -Consider yourself warned. Usage ------ @@ -83,8 +77,11 @@ Create a GitHub repo and push it there:: Now take a look at your repo. Don't forget to carefully look at the generated README. Awesome, right? -It's time to write the code!!! +If you check out your new application, you will see that you can find documentation in the "docs" directory that explains everything about your app as it exists in its initial cookiecutter state, including how to deploy it. +If you want to read the documentation from your browser instead of a text editing window, you'll have to compile it first. First, make sure that you have all of the local packages that you need (including the ones you need to generate this documentation) by running "pip install -r requirements/local.txt". Go into the docs directory and run "make html". That will generate html from the .rst files. Point your browser to your local variant of "/path/to/my_cookiecutter_project/docs/_build/html/index.html". + +So, you now have a simple django application. Go forth and code! "Your Stuff" ------------- diff --git a/{{cookiecutter.repo_name}}/README.rst b/{{cookiecutter.repo_name}}/README.rst index c3007886..a9537eaa 100644 --- a/{{cookiecutter.repo_name}}/README.rst +++ b/{{cookiecutter.repo_name}}/README.rst @@ -59,3 +59,4 @@ Run these commands to deploy the project to Heroku: heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings +For instructions on how to deploy this project to Heroku, look in docs/deploy.rst. diff --git a/{{cookiecutter.repo_name}}/docs/deploy.rst b/{{cookiecutter.repo_name}}/docs/deploy.rst index 1e642c79..eda5220c 100644 --- a/{{cookiecutter.repo_name}}/docs/deploy.rst +++ b/{{cookiecutter.repo_name}}/docs/deploy.rst @@ -1,4 +1,139 @@ Deploy ======== -This is where you describe how the project is deployed in production. +From within your project's directory, run this command to create a Heroku app:: + + $ heroku create {{cookiecutter.project_name}} + Creating {{cookiecutter.project_name}}... done, stack is cedar + http://{{cookiecutter.project_name}}.herokuapp.com/ | git@heroku.com:{{cookiecutter.project_name}}.git + +If you haven't cloned the project from an existing git repo, then you need to initialize it:: + + $ cd {{cookiecutter.repo_name}} + $ git init + Initialized empty Git repository in /path/to/your/project/{{cookiecutter.repo_name}}/.git/ + +Add the Heroku git repo as a remote, so that we can push to it:: + + $ git remote add heroku git@heroku.com:{{cookiecutter.project_name}}.git + +Add a PostgreSQL database. Note that you will probably get a color other than "GOLD". This is normal:: + + $ heroku addons:add heroku-postgresql:dev + Adding heroku-postgresql:dev on {{cookiecutter.project_name}}... done, v3 (free) + Attached as HEROKU_POSTGRESQL_GOLD_URL + Database has been created and is available + ! This database is empty. If upgrading, you can transfer + ! data from another database with pgbackups:restore. + +Add pgbackups to handle backups of the PostgreSQL database:: + + $ heroku addons:add pgbackups + Adding pgbackups on {{cookiecutter.project_name}}... done, v4 (free) + You can now use "pgbackups" to backup your databases or import an external backup. + Use `heroku addons:docs pgbackups` to view documentation. + +Add sendgrid to handle the sending of emails:: + + $ heroku addons:add sendgrid:starter + Adding sendgrid:starter on {{cookiecutter.project_name}}... done, v5 (free) + Use `heroku addons:docs sendgrid` to view documentation. + +Add memcachier for memcached service:: + + $ heroku addons:add memcachier:dev + Adding memcachier:dev on {{cookiecutter.project_name}}... done, v7 (free) + MemCachier is now up and ready to go. Happy bananas! + Use `heroku addons:docs memcachier` to view documentation. + +Look up the name of your database. You should have gotten the name as part of the feedback when you created the +database, but just in case you lost that record:: + + $ heroku pg + === HEROKU_POSTGRESQL_GOLD_URL (DATABASE_URL) + Plan: Dev + Status: available + Connections: 1 + PG Version: 9.2.4 + Created: 2013-09-29 02:00 UTC + Data Size: 6.3 MB + Tables: 0 + Rows: 0/10000 (In compliance) + Fork/Follow: Unsupported + +Promote the database you just created. Please note that your database might be called something other than "GOLD":: + + $ heroku pg:promote HEROKU_POSTGRESQL_GOLD + Promoting HEROKU_POSTGRESQL_GOLD_URL to DATABASE_URL... done + +Set the DJANGO_CONFIGURATION environment variable so that Heroku knows we're in production:: + + $ heroku config:add DJANGO_CONFIGURATION=Production + Setting config vars and restarting {{cookiecutter.project_name}}... done, v8 + DJANGO_CONFIGURATION: Production + +Don't forget to replace the secret key with a random string:: + + $ heroku config:add DJANGO_SECRET_KEY='!!!REPLACE-ME!!!' + Setting config vars and restarting {{cookiecutter.project_name}}... done, v9 + DJANGO_SECRET_KEY: abcdefghijklmnopqrstuvwxyz + +If you're using AWS S3 to serve up static assets, then you need to set these values:: + + $ heroku config:add DJANGO_AWS_ACCESS_KEY_ID=YOUR_ID + $ heroku config:add DJANGO_AWS_SECRET_ACCESS_KEY=YOUR_KEY + $ heroku config:add DJANGO_AWS_STORAGE_BUCKET_NAME=BUCKET + +Commit all the files in your project, and now we're finally ready to push the code to Heroku!:: + + $ git commit -a + $ git push heroku master + Counting objects: 75, done. + Delta compression using up to 8 threads. + Compressing objects: 100% (67/67), done. + Writing objects: 100% (75/75), 28.12 KiB, done. + Total 75 (delta 4), reused 0 (delta 0) + + -----> Python app detected + -----> No runtime.txt provided; assuming python-2.7.4. + -----> Preparing Python runtime (python-2.7.4) + -----> Installing Distribute (0.6.36) + -----> Installing Pip (1.3.1) + -----> Noticed pylibmc. Bootstrapping libmemcached. + -----> Installing dependencies using Pip (1.3.1) + ... + Successfully installed pylibmc django django-configurations django-secure django-cache-url dj-database-url django-braces django-crispy-forms django-floppyforms South django-model-utils Pillow django-allauth psycopg2 unicode-slugify django-autoslug django-avatar gunicorn django-storages gevent boto six python-openid requests-oauthlib requests django-appconf greenlet oauthlib + Cleaning up... + -----> Discovering process types + Procfile declares types -> web + + -----> Compiled slug size: 40.8MB + -----> Launching... done, v10 + http://{{cookiecutter.project_name}}.herokuapp.com deployed to Heroku + + To git@heroku.com:{{cookiecutter.project_name}}.git + * [new branch] master -> master + +Run the syncdb, migrate and collectstatic Django management commands:: + + $ heroku run python {{cookiecutter.repo_name}}/manage.py syncdb --noinput --settings=config.settings + $ heroku run python {{cookiecutter.repo_name}}/manage.py migrate --settings=config.settings + $ heroku run python {{cookiecutter.repo_name}}/manage.py collectstatic --settings=config.settings + +TODO: Explain how to serve static files with dj-static_. + +.. _dj-static: https://github.com/kennethreitz/dj-static + +Open up a django shell on your heroku instance:: + + $ heroku run python {{cookiecutter.repo_name}}/manage.py shell --settings=config.settings + +Run the following lines of code from within that shell: (TODO - automate this) + +.. code-block:: python + + from django.contrib.sites.models import Site + site = Site.objects.get() + site.domain = "{{cookiecutter.domain_name}}" + site.name = "{{cookiecutter.project_name}}" + site.save() diff --git a/{{cookiecutter.repo_name}}/docs/index.rst b/{{cookiecutter.repo_name}}/docs/index.rst index ff8405fb..45501efb 100644 --- a/{{cookiecutter.repo_name}}/docs/index.rst +++ b/{{cookiecutter.repo_name}}/docs/index.rst @@ -4,7 +4,6 @@ contain the root `toctree` directive. Welcome to {{ cookiecutter.project_name }}'s documentation! -==================================================================== Contents: diff --git a/{{cookiecutter.repo_name}}/requirements/base.txt b/{{cookiecutter.repo_name}}/requirements/base.txt index b45127a5..632c2b93 100644 --- a/{{cookiecutter.repo_name}}/requirements/base.txt +++ b/{{cookiecutter.repo_name}}/requirements/base.txt @@ -1,5 +1,6 @@ -# Bleeding edge Django -https://github.com/django/django/archive/1.6c1.tar.gz + +# Latest Django 1.5.x +django==1.5.4 # Configuration django-configurations==0.5.1 @@ -14,9 +15,7 @@ django-floppyforms==1.1 # Models South==0.8.2 -# hack so django-model-utils doesn't override Django 1.6b4 install -git+git://github.com/pydanny/django-model-utils@d71bf2db7581d91dcc8e0a66fd4859eb8969d256 - +django-model-utils==1.5.0 # images Pillow==2.1.0 diff --git a/{{cookiecutter.repo_name}}/requirements/production.txt b/{{cookiecutter.repo_name}}/requirements/production.txt index dcaeea79..72151eb2 100644 --- a/{{cookiecutter.repo_name}}/requirements/production.txt +++ b/{{cookiecutter.repo_name}}/requirements/production.txt @@ -5,4 +5,5 @@ gunicorn==0.17.4 django-storages==1.1.4 gevent==0.13.8 -boto==2.9.5 \ No newline at end of file +boto==2.9.5 +dj-static==0.0.5 \ No newline at end of file diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py index 446f26ef..8026428b 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/settings.py @@ -133,7 +133,7 @@ class Common(Configuration): ########## GENERAL CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone - TIME_ZONE = 'America/Los_Angeles' + TIME_ZONE = 'America/New_York' # See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code LANGUAGE_CODE = 'en-us' @@ -280,8 +280,7 @@ class Local(Common): ########## END INSTALLED_APPS ########## Mail settings - EMAIL_HOST = "localhost" - EMAIL_PORT = 1025 + EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' ########## End mail settings ########## django-debug-toolbar @@ -296,6 +295,32 @@ class Local(Common): } ########## end django-debug-toolbar + ########## STATIC FILE CONFIGURATION + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root + STATIC_ROOT = 'staticfiles' + + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url + STATIC_URL = '/static/' + + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS + STATICFILES_DIRS = ( + join(BASE_DIR, '..', 'static'), + ) + + # as recommended on # https://devcenter.heroku.com/articles/django-assets + # PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) + + # STATICFILES_DIRS = ( + # join(PROJECT_PATH, 'static'), + # ) + + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders + STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + ) + ########## END STATIC FILE CONFIGURATION + ########## Your local stuff: Below this line define 3rd party libary settings @@ -331,31 +356,62 @@ class Production(Common): INSTALLED_APPS += ("gunicorn", ) - ########## STORAGE CONFIGURATION - # See: http://django-storages.readthedocs.org/en/latest/index.html - INSTALLED_APPS += ( - 'storages', - ) - - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' - - # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings - AWS_ACCESS_KEY_ID = values.SecretValue() - AWS_SECRET_ACCESS_KEY = values.SecretValue() - AWS_STORAGE_BUCKET_NAME = values.SecretValue() - AWS_AUTO_CREATE_BUCKET = True - AWS_QUERYSTRING_AUTH = False - - # AWS cache settings, don't change unless you know what you're doing: - AWS_EXPIREY = 60 * 60 * 24 * 7 - AWS_HEADERS = { - 'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, - AWS_EXPIREY) - } + ########## STATIC FILE CONFIGURATION + # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root + STATIC_ROOT = 'staticfiles' # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url - STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + STATIC_URL = '/static/' + + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS + STATICFILES_DIRS = ( + join(BASE_DIR, '..', 'static'), + ) + + # as recommended on # https://devcenter.heroku.com/articles/django-assets + # PROJECT_PATH = os.path.dirname(os.path.abspath(__file__)) + + # STATICFILES_DIRS = ( + # join(PROJECT_PATH, 'static'), + # ) + + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders + STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + ) + + # TODO: check to see if AWS keys are set and if they are, serve static assets using S3 + # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + # AWS_ACCESS_KEY_ID = values.SecretValue() + + # AWS_SECRET_ACCESS_KEY = values.SecretValue() + # AWS_STORAGE_BUCKET_NAME = values.SecretValue() + # AWS_AUTO_CREATE_BUCKET = True + # AWS_QUERYSTRING_AUTH = False + + # # AWS cache settings, don't change unless you know what you're doing: + # AWS_EXPIREY = 60 * 60 * 24 * 7 + # AWS_HEADERS = { + # 'Cache-Control': 'max-age=%d, s-maxage=%d, must-revalidate' % (AWS_EXPIREY, + # AWS_EXPIREY) + # } + + # ########## STORAGE CONFIGURATION + # # See: http://django-storages.readthedocs.org/en/latest/index.html + # INSTALLED_APPS += ( + # 'storages', + # ) + + # # See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings + # STATICFILES_STORAGE = DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' + + # # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url + # STATIC_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME + + # # TODO: check that this actually works + # MEDIA_URL = 'https://s3.amazonaws.com/%s/media/' % AWS_STORAGE_BUCKET_NAME + ########## END STORAGE CONFIGURATION ########## EMAIL diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py index f2f5b8fb..a68c5a70 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/config/wsgi.py @@ -26,7 +26,12 @@ os.environ.setdefault("DJANGO_CONFIGURATION", "Local") # file. This includes Django's development server, if the WSGI_APPLICATION # setting points here. from configurations.wsgi import get_wsgi_application -application = get_wsgi_application() + +# serve static assets using wsgi +# https://devcenter.heroku.com/articles/django-assets +from dj_static import Cling + +application = Cling(get_wsgi_application()) # Apply WSGI middleware here. # from helloworld.wsgi import HelloWorldApplication