├── .gitignore ├── .static └── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.rst ├── applications.txt ├── apps ├── distribution.txt └── documentation.txt ├── code.txt ├── conf.py ├── deployment ├── bootstrap.txt ├── index.txt └── servers.txt ├── examples ├── nginx.conf ├── nginx_ssl.conf └── upstart.conf ├── index.txt └── projects.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | local_conf.py 4 | ll_theme 5 | .build 6 | -------------------------------------------------------------------------------- /.static/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lincolnloop/django-best-practices/f16fd8abb04aa942347fee917324627d2ad38725/.static/.gitignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | install: 5 | - pip install Sphinx==1.1.3 --use-mirrors 6 | script: make html 7 | notifications: 8 | irc: "irc.freenode.org#lincolnloop" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 2 | http://creativecommons.org/licenses/by-nc-sa/3.0/ -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | 9 | # Internal variables. 10 | PAPEROPT_a4 = -D latex_paper_size=a4 11 | PAPEROPT_letter = -D latex_paper_size=letter 12 | ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 13 | 14 | .PHONY: help clean html web pickle htmlhelp latex changes linkcheck 15 | 16 | help: 17 | @echo "Please use \`make ' where is one of" 18 | @echo " html to make standalone HTML files" 19 | @echo " pickle to make pickle files" 20 | @echo " json to make JSON files" 21 | @echo " htmlhelp to make HTML files and a HTML help project" 22 | @echo " epub to make an epub" 23 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 24 | @echo " changes to make an overview over all changed/added/deprecated items" 25 | @echo " linkcheck to check all external links for integrity" 26 | 27 | clean: 28 | -rm -rf .build/* 29 | 30 | html: 31 | mkdir -p .build/html .build/doctrees 32 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html 33 | @echo 34 | @echo "Build finished. The HTML pages are in .build/html." 35 | 36 | pickle: 37 | mkdir -p .build/pickle .build/doctrees 38 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle 39 | @echo 40 | @echo "Build finished; now you can process the pickle files." 41 | 42 | web: pickle 43 | 44 | json: 45 | mkdir -p .build/json .build/doctrees 46 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json 47 | @echo 48 | @echo "Build finished; now you can process the JSON files." 49 | 50 | htmlhelp: 51 | mkdir -p .build/htmlhelp .build/doctrees 52 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp 53 | @echo 54 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 55 | ".hhp project file in .build/htmlhelp." 56 | 57 | latex: 58 | mkdir -p .build/latex .build/doctrees 59 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex 60 | @echo 61 | @echo "Build finished; the LaTeX files are in .build/latex." 62 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 63 | "run these through (pdf)latex." 64 | 65 | epub: 66 | mkdir -p .build/epub .build/doctrees 67 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) .build/epub 68 | @echo 69 | @echo "Build finished. The e-Pub book is in .build/epub." 70 | 71 | changes: 72 | mkdir -p .build/changes .build/doctrees 73 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes 74 | @echo 75 | @echo "The overview file is in .build/changes." 76 | 77 | linkcheck: 78 | mkdir -p .build/linkcheck .build/doctrees 79 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck 80 | @echo 81 | @echo "Link check complete; look for any errors in the above output " \ 82 | "or in .build/linkcheck/output.txt." 83 | 84 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png 2 | :alt: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported 3 | 4 | This project started as a fork of the great `django-reusable-app-docs `_ project started by `Brian Rosner `__ and `Eric Holscher `__ regarding best practices for writing and maintaining reusable Django apps. 5 | 6 | We wanted to expand on the idea and create general "best practices" documentation for Django Web development encompassing ideas that are outside the scope of the official documentation. For more information, `read the project announcement `_. 7 | -------------------------------------------------------------------------------- /applications.txt: -------------------------------------------------------------------------------- 1 | .. _apps-index: 2 | 3 | Django Applications 4 | =================== 5 | 6 | A Django project typically consists of many applications declared in ``INSTALLED_APPS``. Django applications should follow the Unix philosopy of, "Do one thing and do it well." [#unix]_, with a focus on being small and modular, mirroring Django's "loose coupling" design philosophy [#loose-coupling]_. 7 | 8 | James Bennett's `Reusable Apps talk `__ at the first DjangoCon is an excellent primer on the subject of building good Django applications. 9 | 10 | .. [#unix] http://en.wikipedia.org/wiki/Unix_philosophy#McIlroy:_A_Quarter_Century_of_Unix 11 | .. [#loose-coupling] https://docs.djangoproject.com/en/dev/misc/design-philosophies/#loose-coupling 12 | 13 | 14 | Code Organization 15 | ----------------- 16 | 17 | The only requirement of a Django application is that it provides a ``models.py`` file. In practice, however, Django applications are made up of many different files. When building your own applications, follow common file naming conventions. Start with the framework Django provides via ``manage.py startapp `` and build out from there as needed. 18 | 19 | * ``__init__.py`` 20 | * ``admin.py`` 21 | * ``context_processors.py`` 22 | * ``feeds.py`` 23 | * ``forms.py`` 24 | * ``managers.py`` 25 | * ``middleware.py`` 26 | * ``models.py`` 27 | * ``receivers.py`` 28 | * ``signals.py`` 29 | * ``templates/app_name/`` 30 | * ``templatetags/`` 31 | 32 | * ``__init__.py`` 33 | * ``app_name.py`` 34 | 35 | * ``tests.py`` or ``tests/`` 36 | * ``urls.py`` 37 | * ``views.py`` 38 | 39 | What lives in each of these files should be self-explanatory. Let's dive into some of the meatier ones though. 40 | 41 | Models 42 | ------ 43 | 44 | Style 45 | ^^^^^ 46 | 47 | Follow Django's `defined conventions `__ for model code. 48 | 49 | Make 'em Fat 50 | ^^^^^^^^^^^^ 51 | 52 | A common pattern in MVC-style programming is to build thick/fat models and thin controllers. For Django this translates to building models with lots of small methods attached to them and views which use those methods to keep their logic as minimal as possible. There are lots of benefits to this approach. 53 | 54 | 1. **DRY**: Rather than repeating the same logic in multiple views, it is defined once on the model. 55 | 2. **Testable**: Breaking up logic into small methods on the model makes your code easier to unit test. 56 | 3. **Readable**: By giving your methods friendly names, you can abstract ugly logic into something that is easily readable and understandable. 57 | 58 | For a good example of a fat model in Django, look at |the definition of django.contrib.auth.models.User|_. 59 | 60 | .. |the definition of django.contrib.auth.models.User| replace:: the definition of ``django.contrib.auth.models.User`` 61 | .. _the definition of django.contrib.auth.models.User: https://github.com/django/django/blob/ff6ee5f06c2850f098863d4a747069e10727293e/django/contrib/auth/models.py#L225-404 62 | 63 | Managers 64 | -------- 65 | 66 | Similar to models, it's good practice to abstract common logic into methods on a manager. More specifically, you'll probably want a chainable method that you can use on any queryset. This involves some boilerplate that I always forget, so here's an example for (mostly) cutting and pasting:: 67 | 68 | import datetime 69 | from django.db import models 70 | from django.db.models.query import QuerySet 71 | 72 | class PostQuerySet(QuerySet): 73 | def live(self): 74 | """Filter out posts that aren't ready to be published""" 75 | now = datetime.datetime.now() 76 | return self.filter(date_published__lte=now, status="published") 77 | 78 | class PostManager(models.Manager): 79 | def get_query_set(self): 80 | return PostQuerySet(self.model) 81 | def __getattr__(self, attr, *args): 82 | # see https://code.djangoproject.com/ticket/15062 for details 83 | if attr.startswith("_"): 84 | raise AttributeError 85 | return getattr(self.get_query_set(), attr, *args) 86 | 87 | class Post(models.Model): 88 | # field definitions... 89 | objects = PostManager() 90 | 91 | This code will let you call our new method `live` both directly on the manager ``Post.objects.live()`` and chain it on a queryset ``Post.objects.filter(category="tech").live()``. At the time of writing, there is `an open bug `__ to make this less painful. 92 | -------------------------------------------------------------------------------- /apps/distribution.txt: -------------------------------------------------------------------------------- 1 | .. _apps-distribution: 2 | 3 | How do I distribute my app? 4 | =========================== 5 | 6 | Django should be using the standard `Python Package Index 7 | `__ aka PyPI or the Cheese Shop. Eric Holscher wrote 8 | a `tutorial `__ about how to easily package 10 | and upload your app to PyPI. All reusable apps should be uploading to 11 | PyPI. 12 | 13 | If you upload your app to PyPI, it is generally a good idea to prefix 14 | your project name with "django-" 15 | 16 | Also note, that when below when we refer to the default place for 17 | something as a file, that also means that you can make a directory of 18 | that same name; as per normal python. -------------------------------------------------------------------------------- /apps/documentation.txt: -------------------------------------------------------------------------------- 1 | .. _apps-documentation: 2 | 3 | 4 | Documentation 5 | ============= 6 | 7 | 8 | + Placed in a docs directory at the same level as the APP directory 9 | (you do have a top-level directory above your app, right?) 10 | + Can contain templates, for reference 11 | -------------------------------------------------------------------------------- /code.txt: -------------------------------------------------------------------------------- 1 | .. _code-index: 2 | 3 | .. index:: Coding Conventions, PEP 8, PEP 20 4 | pair: Python; Coding Conventions 5 | pair: Django; Coding Conventions 6 | 7 | 8 | Coding Style 9 | ============ 10 | 11 | In general, code should be clean, concise and readable. `The Zen of Python (PEP 20) `_ is a great introduction to best coding practices for Python. 12 | 13 | + Follow the `Style Guide for Python Code (PEP 8) 14 | `__ as closely as 15 | reasonable. 16 | + Follow the `Django coding style 17 | `__. -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-reusable-app-docs documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Dec 28 23:50:42 2008. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # The contents of this file are pickled, so don't put values in the namespace 9 | # that aren't pickleable (module imports are okay, they're removed automatically). 10 | # 11 | # Note that not all possible configuration values are present in this 12 | # autogenerated file. 13 | # 14 | # All configuration values have a default; values that are commented out 15 | # serve to show the default. 16 | 17 | import sys, os 18 | 19 | # If your extensions are in another directory, add it here. If the directory 20 | # is relative to the documentation root, use os.path.abspath to make it 21 | # absolute, like shown here. 22 | #sys.path.append(os.path.abspath('.')) 23 | 24 | # General configuration 25 | # --------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = [] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['.templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.txt' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'Django Best Practices' 45 | copyright = u'2008, Brian Rosner' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '0.1' 53 | # The full version, including alpha/beta/rc tags. 54 | release = '0.1.0' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of documents that shouldn't be included in the build. 67 | #unused_docs = [] 68 | 69 | # List of directories, relative to source directory, that shouldn't be searched 70 | # for source files. 71 | exclude_trees = ['.build'] 72 | 73 | # The reST default role (used for this markup: `text`) to use for all documents. 74 | #default_role = None 75 | 76 | # If true, '()' will be appended to :func: etc. cross-reference text. 77 | #add_function_parentheses = True 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | #add_module_names = True 82 | 83 | # If true, sectionauthor and moduleauthor directives will be shown in the 84 | # output. They are ignored by default. 85 | #show_authors = False 86 | 87 | # The name of the Pygments (syntax highlighting) style to use. 88 | pygments_style = 'sphinx' 89 | 90 | 91 | # Options for HTML output 92 | # ----------------------- 93 | 94 | # The style sheet to use for HTML and HTML Help pages. A file of that name 95 | # must exist either in Sphinx' static/ path, or in one of the custom paths 96 | # given in html_static_path. 97 | #html_style = 'default.css' 98 | 99 | # The name for this set of Sphinx documents. If None, it defaults to 100 | # " v documentation". 101 | html_title = "Django Best Practices" 102 | 103 | # A shorter title for the navigation bar. Default is the same as html_title. 104 | #html_short_title = None 105 | 106 | # The name of an image file (relative to this directory) to place at the top 107 | # of the sidebar. 108 | #html_logo = None 109 | 110 | # The name of an image file (within the static path) to use as favicon of the 111 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 112 | # pixels large. 113 | #html_favicon = None 114 | 115 | # Add any paths that contain custom static files (such as style sheets) here, 116 | # relative to this directory. They are copied after the builtin static files, 117 | # so a file named "default.css" will overwrite the builtin "default.css". 118 | html_static_path = ['.static'] 119 | 120 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 121 | # using the given strftime format. 122 | html_last_updated_fmt = '%b %d, %Y' 123 | 124 | # If true, SmartyPants will be used to convert quotes and dashes to 125 | # typographically correct entities. 126 | #html_use_smartypants = True 127 | 128 | # Custom sidebar templates, maps document names to template names. 129 | #html_sidebars = {} 130 | 131 | # Additional templates that should be rendered to pages, maps page names to 132 | # template names. 133 | #html_additional_pages = {} 134 | 135 | # If false, no module index is generated. 136 | #html_use_modindex = True 137 | 138 | # If false, no index is generated. 139 | #html_use_index = True 140 | 141 | # If true, the index is split into individual pages for each letter. 142 | #html_split_index = False 143 | 144 | # If true, the reST sources are included in the HTML build as _sources/. 145 | #html_copy_source = True 146 | 147 | # If true, an OpenSearch description file will be output, and all pages will 148 | # contain a tag referring to it. The value of this option must be the 149 | # base URL from which the finished HTML is served. 150 | #html_use_opensearch = '' 151 | 152 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 153 | #html_file_suffix = '' 154 | 155 | # Output file base name for HTML help builder. 156 | htmlhelp_basename = 'django-reusable-app-docsdoc' 157 | 158 | 159 | # Options for LaTeX output 160 | # ------------------------ 161 | 162 | # The paper size ('letter' or 'a4'). 163 | #latex_paper_size = 'letter' 164 | 165 | # The font size ('10pt', '11pt' or '12pt'). 166 | #latex_font_size = '10pt' 167 | 168 | # Grouping the document tree into LaTeX files. List of tuples 169 | # (source start file, target name, title, author, document class [howto/manual]). 170 | latex_documents = [ 171 | ('index', 'django-reusable-app-docs.tex', ur'django-reusable-app-docs Documentation', 172 | ur'Brian Rosner', 'manual'), 173 | ] 174 | 175 | # The name of an image file (relative to this directory) to place at the top of 176 | # the title page. 177 | #latex_logo = None 178 | 179 | # For "manual" documents, if this is true, then toplevel headings are parts, 180 | # not chapters. 181 | #latex_use_parts = False 182 | 183 | # Additional stuff for the LaTeX preamble. 184 | #latex_preamble = '' 185 | 186 | # Documents to append as an appendix to all manuals. 187 | #latex_appendices = [] 188 | 189 | # If false, no module index is generated. 190 | #latex_use_modindex = True 191 | 192 | # use local_conf.py file for configuration tweaks 193 | sys.path.append(os.path.dirname(__file__)) 194 | try: 195 | from local_conf import * 196 | except ImportError: 197 | pass -------------------------------------------------------------------------------- /deployment/bootstrap.txt: -------------------------------------------------------------------------------- 1 | .. _deployment-bootstrap: 2 | 3 | Project Bootstrapping 4 | ===================== 5 | 6 | .. index:: virtualenv, pip 7 | 8 | Filesystem Layout 9 | ----------------- 10 | 11 | .. note:: This document is heavily biased towards Unix-style filesystems and may require additional effort to use in other operating systems. 12 | 13 | `Virtualenv `_ is a must for Python projects. It provides a method to isolate different Python environments. We typically host our production sites from ``/opt/webapps/`` and our development sites from ``~/webapps/``. Each individual project gets its own ``virtualenv`` that also serves as the directory for all the source files associated with the project. We use ``pip`` to populate the ``virtualenv`` with the necessary packages. 14 | 15 | The bootstrap process looks like this: 16 | 17 | .. sourcecode:: bash 18 | 19 | cd /opt/webapps 20 | virtualenv mysite.com 21 | cd mysite.com 22 | source bin/activate 23 | pip install -r path/to/requirements.txt 24 | 25 | 26 | .. tip:: For convenience, you can use `virtualenvwrapper `_ which provides some helpers to make working with virtualenvs more friendly. 27 | 28 | .. index:: pip, requirements.txt 29 | 30 | .. _deployment-bootstrap-packaging: 31 | 32 | Packaging 33 | --------- 34 | 35 | One of the keys to successful deployment is to ensure that the software you develop on is as close as possible to the software you deploy on. `Pip `_ provides a simple repeatable method allowing you to consistently deploy Python projects across many machines. Every application that requires third-party libraries should include a `pip requirements file `_ called ``requirements.txt``. Projects should aggregate the application requirements files adding any additional requirements as needed. 36 | 37 | .. rubric:: What to include in your requirements files 38 | 39 | In short, everything. While your operating system may provide some Python packages, nearly everything can be installed cleanly with ``pip`` these days. By installing everything into your virtualenv, you can isolate your environment and prevent system packages from causing version conflicts. 40 | 41 | .. warning:: **Pin your dependencies!** Pip makes it easy to install from a VCS, or just grab whatever version it finds on PyPI. This also makes it easy for your deployments to have different versions of different libraries which can lead to unexpected results. Make sure you specify a version for PyPI libs or a specific commit/tag for VCS checkouts. Examples: ``django==1.4.1`` or ``-e git+https://github.com/toastdriven/django-tastypie.git@v0.9.9#egg=django-tastypie`` -------------------------------------------------------------------------------- /deployment/index.txt: -------------------------------------------------------------------------------- 1 | .. _deployment-index: 2 | 3 | Deployment 4 | ========== 5 | 6 | Contents: 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | :glob: 11 | 12 | * -------------------------------------------------------------------------------- /deployment/servers.txt: -------------------------------------------------------------------------------- 1 | .. _deployment-servers: 2 | 3 | Servers 4 | ======= 5 | 6 | .. note:: Deployment arcitectures vary widely depending on the needs and traffic of the site. The setup described below is minimally configured and works well for most instances. 7 | 8 | We serve Django on Ubuntu Linux with a PostgreSQL database backend via `gunicorn `__ or `uWSGI `__ from behind an `Nginx `__ frontend proxy. For simplicity, we'll only be discussing Gunicorn/Nginx here. 9 | 10 | .. index:: Nginx 11 | 12 | Nginx 13 | ----- 14 | 15 | `Nginx `__ makes for a great frontend server due to its speed, stability and low resource footprint. The typical Nginx configuration for a site looks like this: 16 | 17 | .. literalinclude:: /examples/nginx.conf 18 | :language: nginx 19 | 20 | .. rubric:: What Does it Do? 21 | 22 | The first block tells Nginx where to find the server hosting our Django site. The second block redirects any request coming in on ``www.domain.com`` to ``domain.com`` so each resource has only one canonical URL. The final section is the one that does all the work. It tells Nginx to check if a file matching the request exists in ``/var/www/domain.com``. If it does, it serves that file, if it doesn't, it proxies the request to the Django site. 23 | 24 | .. index:: 25 | pair: Nginx; SSL 26 | 27 | SSL 28 | ^^^ 29 | 30 | Another benefit to running a frontend server is SSL termination. Rather than having two Django instances running for SSL and non-SSL access, we can have Nginx act as the gatekeeper redirecting all requests back to a single non-SSL WSGI instance listening on the ``localhost``. Here's what that would look like: 31 | 32 | .. literalinclude:: /examples/nginx_ssl.conf 33 | :language: nginx 34 | 35 | You can include this code at the bottom of your non-SSL configuration file. 36 | 37 | .. index:: Gunicorn 38 | 39 | Gunicorn 40 | -------- 41 | 42 | Gunicorn is a lightweight WSGI server that can scale from small deploys to high-traffic sites. You can install it via ``pip install gunicorn``. Since Nginx will be listening for HTTP(S) requests, you'll need to bind Gunicorn to a different port. While you're at it, you can tell it to only respond to the ``localhost``. A simple gunicorn process might look like this: 43 | 44 | .. sourcecode:: bash 45 | 46 | $ gunicorn --workers=4 --bind=127.0.0.1:9000 my_project.wsgi:application 47 | 48 | This spawns a gunicorn process with 4 workers listening on http://127.0.0.1:9000. If your project doesn't already have a ``wsgi.py`` file, you'll want to add one. See `the Django WSGI docs `__ or `django-layout `__ for an example. 49 | 50 | Process Management 51 | ^^^^^^^^^^^^^^^^^^ 52 | 53 | You want to be sure that gunicorn is always running and that it starts up automatically after a server reboot. If you are deploying to Ubuntu, ``upstart`` is probably the easiest way to get started. Here is a sample config: 54 | 55 | .. literalinclude:: /examples/upstart.conf 56 | :language: bash 57 | 58 | Save this file to ``/etc/init/gunicorn.conf`` and run ``sudo start gunicorn``. For troubleshooting, your logs will be visible at ``/var/log/upstart/gunicorn.log``. 59 | 60 | .. note:: `Supervisor `__ is a pure Python option if you don't have access to ``upstart``. 61 | -------------------------------------------------------------------------------- /examples/nginx.conf: -------------------------------------------------------------------------------- 1 | # Gunicorn server 2 | upstream django { 3 | server domain.com:9000; 4 | } 5 | 6 | # Redirect all requests on the www subdomain to the root domain 7 | server { 8 | listen 80; 9 | server_name www.domain.com; 10 | rewrite ^/(.*) http://domain.com/$1 permanent; 11 | } 12 | 13 | # Serve static files and redirect any other request to Gunicorn 14 | server { 15 | listen 80; 16 | server_name domain.com; 17 | root /var/www/domain.com/; 18 | access_log /var/log/nginx/domain.com.access.log; 19 | error_log /var/log/nginx/domain.com.error.log; 20 | 21 | # Check if a file exists at /var/www/domain/ for the incoming request. 22 | # If it doesn't proxy to Gunicorn/Django. 23 | try_files $uri @django; 24 | 25 | # Setup named location for Django requests and handle proxy details 26 | location @django { 27 | proxy_pass http://django; 28 | proxy_redirect off; 29 | proxy_set_header Host $host; 30 | proxy_set_header X-Real-IP $remote_addr; 31 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/nginx_ssl.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 67.207.128.83:443; #replace with your own ip address 3 | server_name domain.com; 4 | root /var/www/domain.com/; 5 | access_log /var/log/nginx/domain.com.access.log; 6 | 7 | ssl on; 8 | ssl_certificate /etc/nginx/ssl/certs/domain.com.crt; 9 | ssl_certificate_key /etc/nginx/ssl/private/domain.com.key; 10 | ssl_prefer_server_ciphers on; 11 | 12 | # Check if a file exists at /var/www/domain/ for the incoming request. 13 | # If it doesn't proxy to Gunicorn/Django. 14 | try_files $uri @django; 15 | 16 | # Setup named location for Django requests and handle proxy details 17 | location @django { 18 | proxy_pass http://django; 19 | proxy_redirect off; 20 | proxy_set_header Host $host; 21 | proxy_set_header X-Real-IP $remote_addr; 22 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 23 | proxy_set_header X-Forwarded-Protocol ssl; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /examples/upstart.conf: -------------------------------------------------------------------------------- 1 | # logs to /var/log/upstart/my_project.log 2 | 3 | description "my_project" 4 | start on startup 5 | stop on shutdown 6 | 7 | respawn 8 | 9 | # start from virtualenv path 10 | exec /opt/webapps/my_project/bin/gunicorn -w 4 -b 127.0.0.1:9000 my_project.wsgi:application 11 | setuid www-data 12 | -------------------------------------------------------------------------------- /index.txt: -------------------------------------------------------------------------------- 1 | .. _index: 2 | 3 | Django Best Practices 4 | ===================== 5 | 6 | 7 | This is a living document of best practices in developing and deploying with the `Django Web framework `__. These should not be seen as the *right* way or the *only* way to work with Django, but instead best practices we've honed after years of working with the framework. 8 | 9 | It is a fork of the great `django-reusable-app-docs project `__ started by `Brian Rosner `__ and `Eric Holscher `_ regarding best practices for writing and maintaining reusable Django apps. 10 | 11 | .. note:: The source code for this documentation lives on GitHub as `django-best-practices `__ and can be built in a number of formats using `Sphinx `__. 12 | 13 | Feedback 14 | -------- 15 | 16 | See a problem? Disagree with something? Want to see other topics covered? **We'd love to hear your feedback!** `Email us `__ or `file an issue `__. 17 | 18 | 19 | Table of Contents 20 | ================================= 21 | 22 | .. toctree:: 23 | :maxdepth: 2 24 | :glob: 25 | 26 | code 27 | projects 28 | applications 29 | deployment/index -------------------------------------------------------------------------------- /projects.txt: -------------------------------------------------------------------------------- 1 | .. _projects-index: 2 | 3 | Django Projects 4 | =============== 5 | 6 | At its core, a Django project requires nothing more than a settings file. In practice, almost every project consists of the following items: 7 | 8 | * :ref:`projects-settings` 9 | * :ref:`projects-urls` 10 | * :ref:`projects-wsgi` 11 | * :ref:`projects-applications` 12 | * :ref:`projects-templates` 13 | * :ref:`projects-media` 14 | * manage.py 15 | 16 | .. _projects-settings: 17 | 18 | Settings 19 | -------- 20 | 21 | The settings module is the only true requirement for a Django project. Typically, it lives in the root of your project as ``settings.py``. 22 | 23 | .. _projects-settings-multiple: 24 | 25 | Handling Settings for Multiple Environments 26 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 27 | 28 | Django's ``startproject`` command gives you a single ``settings.py`` file. If you're new to Django, stick with the single file while you learn the ropes. As you start to deploy production sites and work with more than one developer, you'll realize the benefit in maintaining multiple settings files. For example, you probably want to run with ``DEBUG`` on locally, but not in production. 29 | 30 | There are numerous ways to handle multiple settings. Whatever solution you choose, it should meet the following requirements: 31 | 32 | * All the important **settings files are version controlled**. If the settings change on your production site, you'll want to know who made the changes and when they were made. 33 | * **All settings inherit from a common base**. If you want to add ``django-debug-toolbar`` to your ``INSTALLED_APPS``, you should be able to do it without redefining all your ``INSTALLED_APPS``. 34 | 35 | If you don't want to think about it, simply use our Django project template when starting new projects. It is ready to support multiple projects out of the gate:: 36 | 37 | django-admin.py startproject --template=https://github.com/lincolnloop/django-layout/tarball/master -e py,rst,example,gitignore my_project_name 38 | 39 | 40 | .. seealso:: 41 | 42 | `Django's Split Settings Wiki `_ 43 | Examples of handling multiple settings 44 | 45 | Handling File Paths 46 | ^^^^^^^^^^^^^^^^^^^ 47 | 48 | One function of your settings is to tell Django where to find things such as your static media and templates. Most likely they'll already live inside your project. If so, let Python generate the absolute path names for you. This makes your project portable across different environments. 49 | 50 | .. code-block:: python 51 | 52 | import os 53 | DIRNAME = os.path.dirname(__file__) 54 | # ... 55 | STATIC_ROOT = os.path.join(DIRNAME, 'static') 56 | 57 | .. _projects-urls: 58 | 59 | URLconf 60 | -------- 61 | 62 | By default, you'll find your URLconf in the root of your project as ``urls.py``. It defines how requests should be routed for your project. 63 | 64 | Keep it Simple 65 | ^^^^^^^^^^^^^^ 66 | 67 | Your project URLconf should simply include URLconfs from your applications whenever possible. This keeps your application logic inside your application and your project simply serves as a pointer to it. 68 | 69 | .. seealso:: 70 | 71 | `Django URL dispatcher documentation `_ 72 | Including other URLconfs 73 | 74 | Handling URLconfs for Multiple Environments 75 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 76 | 77 | Just like your settings module, eventually, you'll come across the need to run different URLconfs for different environments. You may want to use ``admin`` locally, but not once deployed. Django already provides an easy way for you to do this with the `ROOT_URLCONF `_ setting. 78 | 79 | This is basically the same scenario as having :ref:`multiple settings `. You can use the same solution here:: 80 | 81 | myproject 82 | ... 83 | settings/ 84 | __init__.py 85 | base.py <-- shared by all environments 86 | def.py 87 | production.py 88 | urls/ 89 | __init__.py 90 | base.py <-- shared by all environments 91 | dev.py 92 | production.py 93 | ... 94 | 95 | .. seealso:: 96 | 97 | `Our django-layout template `_ 98 | 99 | 100 | .. _projects-wsgi: 101 | 102 | WSGI File 103 | --------- 104 | 105 | The WSGI file tells your WSGI server what it needs to do to serve your project on the web. Django's default ``wsgi.py`` is sufficient for most applications. 106 | 107 | .. _projects-applications: 108 | 109 | Local Applications 110 | ------------------ 111 | 112 | Local applications are Django applications that are domain-specific to your project. They typically live inside the project module and are so closely tied to your project, they would have little use outside of it. 113 | 114 | Local vs. Third Party 115 | ^^^^^^^^^^^^^^^^^^^^^ 116 | 117 | There are hundreds [#f1]_ of open source Django applications available. Before you reinvent the wheel, make sure somebody hasn't already solved your problem by searching on Google or `Django Packages `_. If you find something that will work do **not** put it your project code, instead add it to your :ref:`pip requirements `. 118 | 119 | .. _tip: If you find something that *mostly* works, but just needs a little polish, consider being a good open source citizen and contributing back to the project or creating a public fork which the project owners can pull from. 120 | 121 | .. [#f1] http://djangopackages.com/categories/apps/ 122 | 123 | 124 | The Namespace 125 | ^^^^^^^^^^^^^ 126 | 127 | How local applications should be imported into your project is a source of ongoing debate in the Django community [#f2]_. Fortunately, with the release of Django 1.4, the default ``manage.py`` no longer changes the ``PYTHONPATH`` [#f3]_, making this much less of an issue. 128 | 129 | At Lincoln Loop, we put project applications inside the project namespace. This prevents polluting the global namespace and running into potential naming conflicts. 130 | 131 | .. [#f2] `Discussion on django-developers mailing list regarding project namespaces in the tutorial `_ 132 | .. [#f3] `Django 1.4 manage.py changes `_ 133 | 134 | .. _projects-templates: 135 | 136 | Templates 137 | --------- 138 | 139 | Location 140 | ^^^^^^^^ 141 | 142 | Templates typically live in one of two places, inside the application or at the root level of a project. We recommend keeping all your templates in the project template directory unless you plan on including your application in multiple projects (or developing it as a open source "reusable" application). In that case, it can be helpful to ship with a set of sample templates in the application, allowing it to work out-of-the-box or serving as an example for other developers. 143 | 144 | Naming 145 | ^^^^^^ 146 | 147 | Django's generic views provide an excellent pattern for naming templates. Following design patterns already found in Django can be helpful for a couple reasons. 148 | 149 | 1. They have been well thought out and tested. 150 | 2. It makes your code immediately understandable to new developers picking up your Django code. 151 | 152 | Most generic view templates are named in the format:: 153 | 154 | [application]/[model]_[function].html 155 | 156 | For example, creating a template to list all of the contacts (``Contact`` model) in my address book (``address_book`` application), I would use the following template:: 157 | 158 | address_book/contact_list.html 159 | 160 | Similarly, a detail view of a contact would use:: 161 | 162 | address_book/contact_detail.html 163 | 164 | Not every template you create will map so closely to a single model, however. In those cases, you're on your own for naming, but should still keep your templates in a directory with the same name as your application. 165 | 166 | When using inclusion tags or other other functionality to render partial templates, keep them in an ``includes`` directory inside the application template directory. For example, if I had an inclusion tag to render a contact form inside my address book application, I would create a template for it at:: 167 | 168 | address_book/includes/contact_form.html 169 | 170 | There is no rule (anymore) that templates must have an ``html`` file extension. If you are rendering something else (plain text, JSON, XML, etc), your templates file extension should match that of the content you are generating. 171 | 172 | .. _projects-media: 173 | 174 | Static Media 175 | ------------ 176 | 177 | Static media encompasses all the non-dynamic content needed for your website: CSS, images, JavaScript, Flash, etc. It comes in two flavors, user-generated content and the media needed to render your site. Best practice dictates that *your* static media lives inside your project and your version control system. Certainly, we don't want stuff our users' uploads to go to the same place. As such, we always use ``django.contrib.staticfiles`` [#django.contrib.staticfiles]_. 178 | 179 | In addition to some other slick features, ``staticfiles`` gives you a ``static`` template tag [#static]_ that will properly locate your static files whether they are on your local computer or in a non-local storage on your production system. This leaves ``MEDIA_URL`` and ``MEDIA_ROOT`` to manage user generated content. 180 | 181 | .. seealso:: 182 | 183 | `On Static Media and Django `_ 184 | 185 | .. [#django.contrib.staticfiles] https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/ 186 | .. [#static] https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:templatetag-staticfiles-static --------------------------------------------------------------------------------