146 |
147 | {%- endblock %}
148 |
--------------------------------------------------------------------------------
/app_construction/index.rst:
--------------------------------------------------------------------------------
1 | ========================
2 | Application Construction
3 | ========================
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 | :glob:
8 |
9 | version_reporting
10 | views_as_a_package
11 |
--------------------------------------------------------------------------------
/app_construction/version_reporting.py:
--------------------------------------------------------------------------------
1 | __version_info__ = {
2 | 'major': 0,
3 | 'minor': 1,
4 | 'micro': 0,
5 | 'releaselevel': 'alpha',
6 | 'serial': 1
7 | }
8 |
9 | def get_version(short=False):
10 | assert __version_info__['releaselevel'] in ('alpha', 'beta', 'final')
11 | vers = ["%(major)i.%(minor)i" % __version_info__, ]
12 | if __version_info__['micro']:
13 | vers.append(".%(micro)i" % __version_info__)
14 | if __version_info__['releaselevel'] != 'final' and not short:
15 | vers.append('%s%i' % (__version_info__['releaselevel'][0], __version_info__['serial']))
16 | return ''.join(vers)
17 |
18 | __version__ = get_version()
19 |
--------------------------------------------------------------------------------
/app_construction/version_reporting.rst:
--------------------------------------------------------------------------------
1 | :Author: Corey Oordt
2 |
3 | =================
4 | Version Reporting
5 | =================
6 |
7 | **Contributors:** Corey Oordt, Stefan Foulis
8 |
9 | .. contents::
10 | :local:
11 |
12 |
13 | What problem does this pattern solve?
14 | =====================================
15 |
16 | It provides a flexible method of recording and reporting your application's version.
17 |
18 | When to use it
19 | ==============
20 |
21 | You should use it with any application or project that has specific releases.
22 |
23 | Why should I use it?
24 | ====================
25 |
26 | 1. It is easy to see which version is currently installed somewhere.
27 |
28 | 2. It is easy to import the version into other places, like documentation or packaging.
29 |
30 | 3. It is easy for others to test the version of your code to better handle backwards-compatibility.
31 |
32 |
33 | Implementation
34 | ==============
35 |
36 | `PEP 386 `_ defines the standard way to specify versions within the Python community. The most common scenario is the ``Major.Minor.Micro`` with a possible ``alpha/beta/release candidate`` suffix.
37 |
38 | Examples::
39 |
40 | 1.0
41 | 0.6.1
42 | 2.1.1b1
43 | 0.3rc2
44 |
45 | When recording your version number you should:
46 |
47 | * Put it within the code, so it's accessible after the package is installed
48 |
49 | * Easily retrieve all the individual parts of the version
50 |
51 | * Record the individual version parts as integers (where appropriate) for easy comparison
52 |
53 | * Have a properly formatted string version available
54 |
55 | Putting the version information in your application's ``__init__.py`` is a great, out-of-the-way place.
56 |
57 | Here is an example that conforms to PEP 386:
58 |
59 | .. rst-class:: caption
60 |
61 | **coolapp/__index__.py**
62 |
63 | .. literalinclude:: version_reporting.py
64 | :linenos:
65 |
66 | This sets up a ``__version_info__`` dictionary to hold the version fields, a ``get_version()`` function to format the ``__version_info__`` into a string, and ``__version__``\ , which is the formatted string version. It is similar to Django's method:
67 |
68 | .. rst-class:: caption
69 |
70 | **django/__init__.py**
71 |
72 | .. literalinclude:: version_reporting_django.py
73 | :linenos:
74 |
75 |
76 | How to use it
77 | =============
78 |
79 | Inside your setup.py file
80 | -------------------------
81 |
82 | The ``setup.py`` file needs a version for your application and you can import it directly from your application, ass seen in this example taken from `django-app-skeleton `_\ 's ``setup.py`` file:
83 |
84 | .. rst-class:: caption
85 |
86 | **django-app-skeleton/skel/setup.py**
87 |
88 | .. literalinclude:: version_reporting_appskel.py
89 | :linenos:
90 |
91 |
92 | Inside your Sphinx documentation's conf.py
93 | ------------------------------------------
94 |
95 | Sphinx also likes to have the version of your application in the formatted documentation. Since the ``conf.py`` configuration file is just Python, you can import your version.
96 |
97 | .. rst-class:: caption
98 |
99 | **coolapp/docs/conf.py**
100 |
101 | .. literalinclude:: version_reporting_sphinx.py
102 | :linenos:
103 |
--------------------------------------------------------------------------------
/app_construction/version_reporting_appskel.py:
--------------------------------------------------------------------------------
1 | # ... other stuff above
2 |
3 | setup(
4 | name = "$$$$APP_NAME$$$$",
5 | version = __import__('$$$$PKG_NAME$$$$').get_version().replace(' ', '-'),
6 | url = '$$$$URL$$$$',
7 | author = '$$$$AUTHOR$$$$',
8 | author_email = '$$$$AUTHOR_EMAIL$$$$',
9 | description = DESC,
10 | long_description = get_readme(),
11 | packages = find_packages(),
12 | include_package_data = True,
13 | install_requires = read_file('requirements.txt'),
14 | classifiers = [
15 | 'License :: OSI Approved :: Apache Software License',
16 | 'Framework :: Django',
17 | ],
18 | )
19 |
--------------------------------------------------------------------------------
/app_construction/version_reporting_django.py:
--------------------------------------------------------------------------------
1 | VERSION = (1, 4, 0, 'alpha', 0)
2 |
3 | def get_version():
4 | version = '%s.%s' % (VERSION[0], VERSION[1])
5 | if VERSION[2]:
6 | version = '%s.%s' % (version, VERSION[2])
7 | if VERSION[3:] == ('alpha', 0):
8 | version = '%s pre-alpha' % version
9 | else:
10 | if VERSION[3] != 'final':
11 | version = '%s %s %s' % (version, VERSION[3], VERSION[4])
12 | from django.utils.version import get_svn_revision
13 | svn_rev = get_svn_revision()
14 | if svn_rev != u'SVN-unknown':
15 | version = "%s %s" % (version, svn_rev)
16 | return version
17 |
--------------------------------------------------------------------------------
/app_construction/version_reporting_sphinx.py:
--------------------------------------------------------------------------------
1 | sys.path.append(os.path.abspath('..'))
2 | os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
3 |
4 | import coolapp
5 |
6 | # The version info for the project you're documenting, acts as replacement for
7 | # |version| and |release|, also used in various other places throughout the
8 | # built documents.
9 | #
10 | # The short X.Y version.
11 | version = coolapp.get_version(short=True)
12 | # The full version, including alpha/beta/rc tags.
13 | release = coolapp.get_version()
--------------------------------------------------------------------------------
/app_construction/views_as_a_package.rst:
--------------------------------------------------------------------------------
1 | :Author: Corey Oordt
2 |
3 | ==================
4 | Views as a package
5 | ==================
6 |
7 | **Contributors:** Corey Oordt
8 |
9 | .. contents::
10 | :local:
11 |
12 | What problem does this pattern solve?
13 | =====================================
14 |
15 | Code in ``views.py`` has become unmanageable.
16 |
17 | When to use it
18 | ==============
19 |
20 | You want to refactor your views into several files.
21 |
22 | Why should I use it?
23 | ====================
24 |
25 | This pattern allows for refactoring the view code into several files without effecting the import process in other files. In other words ``from coolapp.views import foo`` still works.
26 |
27 | Implementation
28 | ==============
29 |
30 | .. note::
31 |
32 | When refactoring your code into multiple files, look deeper and see if there are better ways to accomplish the tasks, such as using generic views.
33 |
34 | Python ``import``\ s briefly
35 | ----------------------------
36 |
37 | This pattern takes advantage of the way that Python handles importing items into the local namespace. The statement ``from foo import views`` will work with code organized as:
38 |
39 | .. rst-class:: caption
40 |
41 | **Example 1**
42 |
43 | ::
44 |
45 | foo
46 | ├── __init__.py
47 | └── views.py
48 |
49 | as well as:
50 |
51 | .. rst-class:: caption
52 |
53 | **Example 2**
54 |
55 | ::
56 |
57 | foo
58 | ├── __init__.py
59 | └── views
60 | └── __init__.py
61 |
62 | In the case of Example 2, the contents of ``foo/views/__init__.py`` is executed. The ``__init__.py`` file is going to be important in the switch from a `module `_ (one file) to a `package `_ (directory with ``__init__.py``\ ).
63 |
64 | First rename ``views.py`` to something like ``old_views.py`` to prevent name confusion. Second create the ``views`` directory and add an ``__init__.py`` file. Then refactor the ``old_views.py`` into two or more files. See Example 3.
65 |
66 | .. rst-class:: caption
67 |
68 | **Example 3**
69 |
70 | ::
71 |
72 | foo
73 | ├── __init__.py
74 | ├── old_views.py
75 | └── views
76 | ├── __init__.py
77 | ├── bar.py
78 | └── baz.py
79 |
80 | .. note::
81 |
82 | When refactoring your views, you will probably need to change imports from other modules in your app, such as models. The statement ``from models import Foo`` will no longer work since the ``models.py`` file is not in the same directory.
83 |
84 | Instead, you will need to use a full path import: ``from foo.models import Foo``\ .
85 |
86 | Now, to make imports such as ``from views import bar_detail_view`` work, we need to add a couple of lines to ``views/__init__.py``
87 |
88 | .. rst-class:: caption
89 |
90 | **views/__init__.py**
91 |
92 | .. code-block:: python
93 | :linenos:
94 |
95 | from bar import *
96 | from baz import *
97 |
98 | These statements import all the contents of ``views.bar`` and ``views.baz`` into ``views``\ . You can limit what is imported with ``*`` defining a list named ``__all__`` (see `Importing * from a Package `_\ ) within the module.
99 |
100 | ``__all__`` it is taken to be the list of names that should be imported when ``from module import *`` is encountered. Django uses this often, such as in ``django.conf.urls.defaults``\ .
101 |
102 | .. attention::
103 |
104 | It is up to you to maintain the ``__all__`` list as you update the file.
105 |
106 |
107 | Sources
108 | =======
109 |
110 | http://stackoverflow.com/questions/2675722/django-breaking-up-views
111 |
112 |
113 |
--------------------------------------------------------------------------------
/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # app documentation build configuration file, created by
4 | # sphinx-quickstart on Wed Oct 21 13:18:22 2009.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | sys.path.append(os.path.abspath('..'))
20 | sys.path.append(os.path.abspath('./_plugins'))
21 |
22 | os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
23 |
24 | # -- General configuration -----------------------------------------------------
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = []
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'Django Patterns'
44 | copyright = u'2011, Corey Oordt'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '1.0'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '1.0'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of documents that shouldn't be included in the build.
66 | #unused_docs = []
67 |
68 | # List of directories, relative to source directory, that shouldn't be searched
69 | # for source files.
70 | exclude_trees = ['_build']
71 |
72 | # The reST default role (used for this markup: `text`) to use for all documents.
73 | #default_role = None
74 |
75 | # If true, '()' will be appended to :func: etc. cross-reference text.
76 | #add_function_parentheses = True
77 |
78 | # If true, the current module name will be prepended to all description
79 | # unit titles (such as .. function::).
80 | #add_module_names = True
81 |
82 | # If true, sectionauthor and moduleauthor directives will be shown in the
83 | # output. They are ignored by default.
84 | #show_authors = False
85 |
86 | # The name of the Pygments (syntax highlighting) style to use.
87 | pygments_style = 'sphinx'
88 |
89 | # A list of ignored prefixes for module index sorting.
90 | #modindex_common_prefix = []
91 |
92 |
93 | # -- Options for HTML output ---------------------------------------------------
94 |
95 | # The theme to use for HTML and HTML Help pages. Major themes that come with
96 | # Sphinx are currently 'default' and 'sphinxdoc'.
97 | html_theme = 'default'
98 |
99 | # Theme options are theme-specific and customize the look and feel of a theme
100 | # further. For a list of options available for each theme, see the
101 | # documentation.
102 | #html_theme_options = {}
103 |
104 | # Add any paths that contain custom themes here, relative to this directory.
105 | #html_theme_path = []
106 |
107 | #html_translator_class = 'writers.htmlid.HTMLIdTranslator'
108 |
109 | # The name for this set of Sphinx documents. If None, it defaults to
110 | # " v documentation".
111 | html_title = 'Django Patterns'
112 |
113 | # A shorter title for the navigation bar. Default is the same as html_title.
114 | #html_short_title = None
115 |
116 | # The name of an image file (relative to this directory) to place at the top
117 | # of the sidebar.
118 | #html_logo = None
119 |
120 | # The name of an image file (within the static path) to use as favicon of the
121 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
122 | # pixels large.
123 | #html_favicon = None
124 |
125 | # Add any paths that contain custom static files (such as style sheets) here,
126 | # relative to this directory. They are copied after the builtin static files,
127 | # so a file named "default.css" will overwrite the builtin "default.css".
128 | html_static_path = ['_static']
129 |
130 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
131 | # using the given strftime format.
132 | #html_last_updated_fmt = '%b %d, %Y'
133 |
134 | # If true, SmartyPants will be used to convert quotes and dashes to
135 | # typographically correct entities.
136 | #html_use_smartypants = True
137 |
138 | # Custom sidebar templates, maps document names to template names.
139 | #html_sidebars = {}
140 |
141 | # Additional templates that should be rendered to pages, maps page names to
142 | # template names.
143 | #html_additional_pages = {}
144 |
145 | # If false, no module index is generated.
146 | #html_use_modindex = True
147 |
148 | # If false, no index is generated.
149 | #html_use_index = True
150 |
151 | # If true, the index is split into individual pages for each letter.
152 | #html_split_index = False
153 |
154 | # If true, links to the reST sources are added to the pages.
155 | html_show_sourcelink = False
156 |
157 | # If true, an OpenSearch description file will be output, and all pages will
158 | # contain a tag referring to it. The value of this option must be the
159 | # base URL from which the finished HTML is served.
160 | #html_use_opensearch = ''
161 |
162 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
163 | #html_file_suffix = ''
164 |
165 | html_compact_lists = True
166 |
167 | # Output file base name for HTML help builder.
168 | htmlhelp_basename = 'djangopatternsdoc'
169 |
170 |
171 | # -- Options for LaTeX output --------------------------------------------------
172 |
173 | # The paper size ('letter' or 'a4').
174 | #latex_paper_size = 'letter'
175 |
176 | # The font size ('10pt', '11pt' or '12pt').
177 | #latex_font_size = '10pt'
178 |
179 | # Grouping the document tree into LaTeX files. List of tuples
180 | # (source start file, target name, title, author, documentclass [howto/manual]).
181 | latex_documents = [
182 | ('index', 'djangopatterns.tex', u'Django Patterns',
183 | u'Corey Oordt', 'manual'),
184 | ]
185 |
186 | # The name of an image file (relative to this directory) to place at the top of
187 | # the title page.
188 | #latex_logo = None
189 |
190 | # For "manual" documents, if this is true, then toplevel headings are parts,
191 | # not chapters.
192 | #latex_use_parts = False
193 |
194 | # Additional stuff for the LaTeX preamble.
195 | #latex_preamble = ''
196 |
197 | # Documents to append as an appendix to all manuals.
198 | #latex_appendices = []
199 |
200 | # If false, no module index is generated.
201 | #latex_use_modindex = True
202 |
--------------------------------------------------------------------------------
/configuration/autodiscovery.rst:
--------------------------------------------------------------------------------
1 | =============
2 | Autodiscovery
3 | =============
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more.
7 |
8 | What problem does this pattern solve?
9 | =====================================
10 |
11 | An app provides a service that requires complex configuration or customization by other apps to properly use it.
12 |
13 | When to use it
14 | ==============
15 |
16 | Why should I use it?
17 | ====================
18 |
19 | Implementation
20 | ==============
21 |
22 | For each app, we need to look for an specific module inside that app's package. We can't use os.path here -- recall that modules may be imported different ways (think zip files) -- so we need to get the app's __path__ and look for the module on that path.
23 |
24 | Step 1: find out the app's ``__path__`` Import errors here will (and should) bubble up, but a missing __path__ (which is legal, but weird) fails silently -- apps that do weird things with __path__ might need to roll their own index registration.
25 |
26 | Step 2: use imp.find_module to find the app's search_indexes.py. For some reason imp.find_module raises ImportError if the app can't be found but doesn't actually try to import the module. So skip this app if its search_indexes.py doesn't exist
27 |
28 | Step 3: import the app's search_index file. If this has errors we want them to bubble up.
29 |
30 | .. rst-class:: caption
31 |
32 | **Django Snippet 2404: Generic Autodiscovery**
33 |
34 | .. literalinclude:: autodiscovery1.py
35 | :linenos:
36 |
37 |
38 | How to use it
39 | =============
40 |
41 | Sources
42 | =======
43 |
44 | Useful Links
45 | ============
46 |
47 | * `Generic Autodiscovery `_
48 | * `Looking at registration patterns in Django `_
49 | * `django-config-wizard autodiscover.py `_
50 |
51 | Where is it used?
52 | =================
53 |
54 |
--------------------------------------------------------------------------------
/configuration/autodiscovery1.py:
--------------------------------------------------------------------------------
1 | def generic_autodiscover(module_name):
2 | """
3 | Usage:
4 | generic_autodiscover('commands') <-- find all commands.py and load 'em
5 | """
6 | import imp
7 | from django.conf import settings
8 |
9 | for app in settings.INSTALLED_APPS:
10 | try:
11 | import_module(app)
12 | app_path = sys.modules[app].__path__
13 | except AttributeError:
14 | continue
15 | try:
16 | imp.find_module(module_name, app_path)
17 | except ImportError:
18 | continue
19 | import_module('%s.%s' % (app, module_name))
20 | app_path = sys.modules['%s.%s' % (app, module_name)]
21 |
--------------------------------------------------------------------------------
/configuration/configure_app.rst:
--------------------------------------------------------------------------------
1 | :Author: Corey Oordt
2 |
3 | =========================
4 | Configurable Applications
5 | =========================
6 |
7 | **Contributors:** Corey Oordt
8 |
9 | .. contents::
10 | :local:
11 |
12 | What problem does this pattern solve?
13 | =====================================
14 |
15 | You want to allow configuration of your app without having to modify its code. You may also want to provide reasonable defaults that users can override.
16 |
17 | When to use it
18 | ==============
19 |
20 | Use this whenever project- or implementation-specific information is required at runtime or there are obvious choices or options for the application.
21 |
22 | Good examples:
23 |
24 | * API key
25 | * Debugging flags
26 | * Location(s) to look for files
27 | * Which features should be used (feature flags)
28 |
29 | Implementation
30 | ==============
31 |
32 | Create a ``settings.py`` file in your application
33 |
34 | ::
35 |
36 | coolapp
37 | ├── __init__.py
38 | ├── admin.py
39 | ├── models.py
40 | ├── settings.py
41 | ├── tests.py
42 | └── views.py
43 |
44 | Basic Pattern with one setting
45 | ------------------------------
46 |
47 | Inside the ``settings.py`` file, you will import Django's settings and use ``getattr()`` to retrieve the value, or use a default value. There are several parts to this:
48 |
49 | * **Internal Name:** The name you will use within your application
50 | * **Namespaced Name:** The name used in a project's ``settings.py``\ , with a prefix to avoid collisions.
51 | * **Default Value:** The value for this setting if the namespaced name is not in the project's ``settings.py``\ .
52 |
53 | .. rst-class:: caption
54 |
55 | **coolapp/settings.py**
56 |
57 | .. literalinclude:: configure_app1.py
58 | :linenos:
59 |
60 | Here, ``COOL_WORD`` is the *internal name,* ``COOLAPP_COOL_WORD`` is the *namespaced name,* and ``'cool'`` is the *default value.*
61 |
62 | Requiring a value for a setting
63 | -------------------------------
64 |
65 | For something like an API key, you will want to draw attention if it's empty. You will do this by raising an ``ImproperlyConfigured`` exception.
66 |
67 | .. rst-class:: caption
68 |
69 | **coolapp/settings.py**
70 |
71 | .. literalinclude:: configure_app2.py
72 | :linenos:
73 |
74 | Many settings for your application
75 | ----------------------------------
76 |
77 | Django has internally began using dictionaries for groups of settings, such as ``DATABASES``\ . Django debug toolbar, for example, uses one dictionary to store all its configurations.
78 |
79 | .. rst-class:: caption
80 |
81 | **debug_toolbar/toolbar/loader.py**
82 |
83 | .. literalinclude:: configure_app3.py
84 | :linenos:
85 |
86 | It creates a standard set of configurations in line 13, and then uses the dictionaries ``update()`` method in line 18 to add or override current key/values.
87 |
88 |
89 | Settings with nested dictionaries
90 | ---------------------------------
91 |
92 | If your settings dictionary has a dictionary as a value, you need to take a slightly different approach. ``dict.update()`` will completely overwrite the nested dictionaries, not merge them. To make things trickier, ``dict.update()`` doesn't return a value, so
93 |
94 | .. code-block:: python
95 | :linenos:
96 |
97 | DEFAULT_SETTINGS.update(getattr(settings, 'FOOBAR_SETTINGS', {}))
98 | DEFAULT_SETTINGS['FOO'] = DEFAULT_FOO.update(DEFAULT_SETTINGS.get('FOO', {}))
99 |
100 | leaves ``DEFAULT_SETTINGS['FOO']`` with a value of ``None``\ . So lets try something else.
101 |
102 | .. rst-class:: caption
103 |
104 | **supertagging/settings.py**
105 |
106 | .. literalinclude:: configure_app4.py
107 | :linenos:
108 |
109 |
110 | In this example taken from django-supertagging, line 8 shows the default values for ``SUPERTAGGING_SETTINGS['MARKUP']``\ . Line 16 retrieves the ``SUPERTAGGING_SETTINGS`` dictionary into a temporary variable using ``getattr``\ .
111 |
112 | Line 17 merges the ``DEFAULT_SETTINGS`` dictionary with the dictionary retrieved in line 16 into a new copy. By converting each dictionary into a list of tuple-pairs with the ``items()`` method, it can combine them using the ``+`` operator. When this list is converted back into a dictionary, it uses the last found key-value pair.
113 |
114 | Lines 18-20 merge the defaults for ``MARKUP`` with whatever the user has specified.
115 |
116 | Turning the keys into attributes
117 | --------------------------------
118 | Having one dictionary for all your applications settings is all well and good, but requires more typing. Instead of typing:
119 |
120 | .. code-block:: python
121 | :linenos:
122 |
123 | from settings import USER_SETTINGS
124 | if USER_SETTINGS['ENABLED']:
125 | # do something
126 | pass
127 |
128 | it would be nice to type:
129 |
130 | .. code-block:: python
131 | :linenos:
132 |
133 | from settings import ENABLED
134 | if ENABLED:
135 | # do something
136 | pass
137 |
138 | What we want to do is convert the first set of keys into variables. Python has a built-in function called `globals() `_ that returns a dictionary of the symbol table of the current module.
139 |
140 | If you printed the value of ``globals()`` in an empty Python script, you would see something like:
141 |
142 | .. code-block:: python
143 |
144 | >>> print globals()
145 | {'__builtins__': , '__name__': '__main__', '__doc__': None, '__package__': None}
146 |
147 | Since ``globals()`` returns a dictionary, you can use its ``update()`` method to alter its contents in place.
148 |
149 | .. code-block:: python
150 |
151 | >>> d = {'foo': 1, 'bar': 2}
152 | >>> globals().update(d)
153 | >>> print globals()
154 | {'bar': 2, 'd': {'foo': 1, 'bar': 2}, '__builtins__': , '__name__': '__main__', 'foo': 1, '__doc__': None, '__package__': None}
155 |
156 | .. warning::
157 |
158 | While the effect of this is localized to this module, you must be careful with the names of the dictionary keys. If there is a naming conflict, your dictionary wins. That is probably not what you want.
159 |
160 | Using the suppertagging example above, adding:
161 |
162 | .. code-block:: python
163 | :linenos:
164 |
165 | globals().update(USER_SETTINGS)
166 |
167 | to the bottom of the ``supertagging/settings.py`` file, gives you access to all the top-level keys of ``USER_SETTINGS`` as attributes of ``settings.py``
168 |
169 |
170 | How to use it
171 | =============
172 |
173 | Access to your settings is a simple import. As shown in the previous section, you can import the entire dictionary of settings or, if you added them to the settings module, you can import them individually.
174 |
--------------------------------------------------------------------------------
/configuration/configure_app1.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 | COOL_WORD = getattr(settings, 'COOLAPP_COOL_WORD', 'cool')
4 |
--------------------------------------------------------------------------------
/configuration/configure_app2.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.core.exceptions import ImproperlyConfigured
3 |
4 | API_KEY = getattr(settings, 'COOLAPP_API_KEY', None)
5 |
6 | if API_KEY is None:
7 | raise ImproperlyConfigured("You haven't set 'COOLAPP_API_KEY'.")
8 |
--------------------------------------------------------------------------------
/configuration/configure_app3.py:
--------------------------------------------------------------------------------
1 | """
2 | The main DebugToolbar class that loads and renders the Toolbar.
3 | """
4 | from django.conf import settings
5 | from django.template.loader import render_to_string
6 |
7 | class DebugToolbar(object):
8 |
9 | def __init__(self, request):
10 | self.request = request
11 | self.panels = []
12 | base_url = self.request.META.get('SCRIPT_NAME', '')
13 | self.config = {
14 | 'INTERCEPT_REDIRECTS': True,
15 | 'MEDIA_URL': u'%s/__debug__/m/' % base_url
16 | }
17 | # Check if settings has a DEBUG_TOOLBAR_CONFIG and updated config
18 | self.config.update(getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}))
19 |
20 | # ... more code below
--------------------------------------------------------------------------------
/configuration/configure_app4.py:
--------------------------------------------------------------------------------
1 | DEFAULT_SETTINGS = {
2 | 'ENABLED': False,
3 | 'DEBUG': False,
4 |
5 | # ... other settings
6 | }
7 |
8 | DEFAULT_MARKUP_SETTINGS = {
9 | 'ENABLED': False,
10 | 'FIELD_SUFFIX': "tagged",
11 | 'EXCLUDE': [],
12 | 'CONTENT_CACHE_TIMEOUT': 3600,
13 | 'MIN_RELEVANCE': 0,
14 | }
15 |
16 | temp_settings = getattr(settings, 'SUPERTAGGING_SETTINGS', {})
17 | USER_SETTINGS = dict(DEFAULT_SETTINGS.items() + temp_settings.items())
18 | USER_SETTINGS['MARKUP'] = dict(
19 | DEFAULT_MARKUP_SETTINGS.items() + USER_SETTINGS.get('MARKUP', {}).items()
20 | )
--------------------------------------------------------------------------------
/configuration/index.rst:
--------------------------------------------------------------------------------
1 | ======================
2 | Configuration Patterns
3 | ======================
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 | :glob:
8 |
9 | autodiscovery
10 | configure_app
--------------------------------------------------------------------------------
/decorator_apps/add_fieldsets_to_model_admin.rst:
--------------------------------------------------------------------------------
1 | ===================================
2 | Adding fieldsets to a model's admin
3 | ===================================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | .. code-block:: python
9 |
10 | for model, modeladmin in admin.site._registry.items():
11 | if model in model_registry.values() and modeladmin.fieldsets:
12 | fieldsets = getattr(modeladmin, 'fieldsets', ())
13 | fields = [cat.split('.')[2] for cat in registry if registry[cat] == model]
14 | # check each field to see if already defined
15 | for cat in fields:
16 | for k,v in fieldsets:
17 | if cat in v['fields']:
18 | fields.remove(cat)
19 | # if there are any fields left, add them under the categories fieldset
20 | if len(fields) > 0:
21 | print fields
22 | admin.site.unregister(model)
23 | admin.site.register(model, type('newadmin', (modeladmin.__class__,), {
24 | 'fieldsets': fieldsets + (('Categories', {
25 | 'fields': fields
26 | }),)
27 | }))
28 |
--------------------------------------------------------------------------------
/decorator_apps/change_the_admin_widget.rst:
--------------------------------------------------------------------------------
1 | ==================================
2 | Change the admin widget of a field
3 | ==================================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | Django TinyMCE allows you to add TinyMCE functionality to your app if you make certain modifications to your app. This is great if it is your code. However, it doesn’t work so well, if it is someone else’s code. Justin forked Django-TinyMCE to provide this lazy customization.
9 |
10 | The configuration is simple: the app.model name is the key, and then value is a list of fields to have TinyMCE on in the admin.
11 |
12 | ::
13 |
14 | TINYMCE_ADMIN_FIELDS = {
15 | 'app1.model1': ('body',),
16 | 'app1.model2': ('blog_text', 'blog_teaser')
17 | }
18 |
19 |
20 | There are several steps to this process.
21 |
22 | The first is creating a REGISTRY variable to hold the Model and field specifications in our settings.py
23 |
24 | ::
25 |
26 | from django.db.models import get_model
27 | import django.conf import settings
28 |
29 | REGISTRY = {}
30 | ADMIN_FIELDS = getattr(settings, 'TINYMCE_ADMIN_FIELDS', {})
31 |
32 | for model_name, field in ADMIN_FIELDS.items():
33 | if isinstance(model_name, basestring):
34 | model = get_model(*model_name.split('.'))
35 | if model in registry:
36 | return
37 | REGISTRY[model] = field
38 |
39 |
40 | Next in out admin.py, we declare a Model admin class, with one new attribute: editor_fields. We are also going to override a standard model admin method:
41 |
42 | formfield for dbfield. This is the method that given a database field will return the form field to render.
43 |
44 | our overridden method checks to see if this field is in our list of editor_fields, and if so, returns a version using the TinyMCE widget.
45 |
46 | if the field is not in our list, we punt it back to the super class.
47 |
48 | ``admin.py``
49 |
50 | ::
51 |
52 | # Define a new ModelAdmin subclass
53 |
54 | class TinyMCEAdmin(admin.ModelAdmin):
55 | editor_fields = ()
56 |
57 | def formfield_for_dbfield(self, db_field, **kwargs):
58 | if db_field.name in self.editor_fields:
59 | return db_field.formfield(widget=TinyMCE())
60 | return super(TinyMCEAdmin, self).formfield_for_dbfield(
61 | db_field, **kwargs)
62 |
63 |
64 | œœ
65 | Finally, we put the two pieces together. At the bottom of admin.py we loop through the admin’s current admin registry.
66 |
67 | Check if the current iteration is in our registry
68 |
69 | if it is, we unregister that model’s current admin
70 |
71 | and then re-register the model with a dynamically-created class called newadmin
72 |
73 | that is a subclass of our previously declared admin and the model’s current admin
74 |
75 | and we set that new class’s editor-fields attribute to the fields in our registry
76 |
77 | ``admin.py``
78 |
79 | ::
80 |
81 | for model, modeladmin in admin.site._registry.items():
82 | if model in REGISTRY:
83 | admin.site.unregister(model)
84 | admin.site.register(
85 | model,
86 | type('newadmin',
87 | (TinyMCEAdmin, modeladmin.__class__),
88 | {'editor_fields': REGISTRY[model],}
89 | )
90 | )
91 |
--------------------------------------------------------------------------------
/decorator_apps/index.rst:
--------------------------------------------------------------------------------
1 | ======================
2 | Decorator App Patterns
3 | ======================
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 | :glob:
8 |
9 | introduction
10 | add_fieldsets_to_model_admin
11 | change_the_admin_widget
12 | lazy_field_insertion
13 | lazy_manager_insertion
14 |
--------------------------------------------------------------------------------
/decorator_apps/introduction.rst:
--------------------------------------------------------------------------------
1 | ======================================
2 | Introduction to decorator applications
3 | ======================================
4 |
5 | **Contributors:** Corey Oordt
6 |
7 | .. contents::
8 | :local:
9 |
10 | What is a decorator app?
11 | ========================
12 |
13 | A decorator app is an application that adds functionality to another application without the application's awareness. It is different than a Python decorator, rather it follows the idea from
14 | `Design Patterns: Elements of Reusable Object-Oriented Software `_\ :
15 |
16 | **Intent:** Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
17 |
18 | **Also Known As:** Wrapper
19 |
20 | **Motivation:** Sometimes we want to add responsibilities to individual objects, not to an entire class. A graphical user interface toolkit, for example, should let you add properties like borders or behaviors like scrolling to any user interface component.
21 |
22 | One way to add responsibilities is with inheritance. Inheriting a border from another class puts a border around every subclass instance. This is inflexible, however, because the choice of border is made statically. A client can't control how and when to decorate the component with a border.
23 |
24 | It is different than a true decorator in that Django has no way to wrap models or applications. Instead of wrapping the model, and because we're using a dynamic language like Python, we will inject independent code into the model at runtime.
25 |
26 | Benefits
27 | ========
28 |
29 | Abstraction of metadata
30 | -----------------------
31 |
32 | When developing an application to manage data, images for example, you include data and metadata. The data is the image, or path to that image. Any other information is metadata.
33 |
34 | For this image application, how much metadata do you include? Some metadata may seem straightforward enough to include: name, width, height, resolution and format come to mind. What about less common things such as author, usage license, categories, and tags? And some of that metadata might be shared across other data applications. A video application might also include usage license, categories and tags. Should each application store their metadata separately?
35 |
36 | You can design data applications that store minimal amounts of metadata (metadata that is easily extracted from the image, for example) and leave other metadata to specialized decorator applications.
37 |
38 | Metadata aggregation
39 | --------------------
40 |
41 | It is likely that you would want to manage taxonomy metadata like categories or tags the same way throughout a project. It's rather cumbersome if every third-party application allows for a different system for handling it. A decorator application can provide a single way to manage that metadata and aggregate it throughout a project. It is easy then to query all objects, across all applications, that are tagged *foo*\ , or are categorized as *bar*\ .
42 |
43 | Metadata customization
44 | ----------------------
45 |
46 | ":ref:`no-two-projects-are-alike`\ " I always say, and that includes how they want to handle metadata. A checkbox stating you have reproduction rights might work in one project while another requires a much more specific licensing description. A decorator app for licensing allows the image application to ignore that bit of metadata in its code. When both apps are included in a project, however, the same image application can show different licensing options, depending on the project configuration.
47 |
48 | Alternative data handling
49 | -------------------------
50 |
51 | Decorators aren't just good for metadata; they can also alter how the data is managed. Take an application to handle blog entries, for example. The primary data is the text of the entry. A good question for the application is "How does the user enter text into that field?" Most apps force a decision on the user, such as a markup language such as Textile, reStructuredText, or Markdown or a WYSIWYG editor like TinyMCE.
52 |
53 | If no two projects are alike, might that also include text formatting? In one project, the users might want a WYSIWYG editor, while others prefer a specific markup language. A decorator app can manage that for the data app, especially if the data app includes some hooks to make it easier.
54 |
55 |
56 | Warnings
57 | ========
58 |
59 | Django doesn't have any native way to add functionality to other applications. Therefore accomplishing this task requires modifying class definitions at runtime. Depending on the amount and type of modification, there could be unforeseen consequences.
60 |
61 | All the patterns discussed are used in production and heavily tested. Each pattern does significant error checking to make sure any inserted code doesn't clobber existing functionality.
62 |
--------------------------------------------------------------------------------
/decorator_apps/lazy_field_insertion.rst:
--------------------------------------------------------------------------------
1 | ====================
2 | Lazy Field Insertion
3 | ====================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 |
9 | The idea is allow developers to decide which models will have categories in the project’s settings.py, using a dictionary with the model as the key and the field or fields as the value.
10 |
11 |
12 |
13 | .. code-block:: python
14 |
15 | 'FK_REGISTRY': {
16 | 'flatpages.flatpage': 'category',
17 | 'simpletext.simpletext': (
18 | 'primary_category',
19 | {'name': 'secondary_category', 'related_name': 'simpletext_sec_cat'},
20 | ),
21 | },
22 | 'M2M_REGISTRY': {
23 | 'simpletext.simpletext': {'name': 'categories', 'related_name': 'm2mcats'},
24 | 'flatpages.flatpage': (
25 | {'name': 'other_categories', 'related_name': 'other_cats'},
26 | {'name': 'more_categories', 'related_name': 'more_cats'},
27 | ),
28 | },
29 |
30 |
31 | at the bottom of the category app's __init__.py, you can read the configuration from settings.
32 |
33 | loop through them
34 |
35 | Do a bit of error checking
36 |
37 | Load the model class
38 |
39 | Loop through the given fields
40 |
41 | We make sure that the field doesn't already exist by attempting to get it
42 |
43 | Finally we add the field to the model by instantiating the field and calling its contribute_to_class method.
44 |
45 | .. code-block:: python
46 |
47 | import fields
48 |
49 | from django.db.models import FieldDoesNotExist
50 |
51 | class AlreadyRegistered(Exception):
52 | """
53 | An attempt was made to register a model more than once.
54 | """
55 | pass
56 |
57 | # The field registry keeps track of the individual fields created.
58 | # {'app.model.field': Field(**extra_params)}
59 | # Useful for doing a schema migration
60 | field_registry = {}
61 |
62 | # The model registry keeps track of which models have one or more fields
63 | # registered.
64 | # {'app': [model1, model2]}
65 | # Useful for admin alteration
66 | model_registry = {}
67 |
68 | def register_m2m(model, field_name='categories', extra_params={}):
69 | return _register(model, field_name, extra_params, fields.CategoryM2MField)
70 |
71 | def register_fk(model, field_name='category', extra_params={}):
72 | return _register(model, field_name, extra_params, fields.CategoryFKField)
73 |
74 | def _register(model, field_name, extra_params={}, field=fields.CategoryFKField):
75 | app_label = model._meta.app_label
76 | registry_name = ".".join((app_label, model.__name__, field_name)).lower()
77 |
78 | if registry_name in field_registry:
79 | return #raise AlreadyRegistered
80 | opts = model._meta
81 | try:
82 | opts.get_field(field_name)
83 | except FieldDoesNotExist:
84 | if app_label not in model_registry:
85 | model_registry[app_label] = []
86 | if model not in model_registry[app_label]:
87 | model_registry[app_label].append(model)
88 | field_registry[registry_name] = field(**extra_params)
89 | field_registry[registry_name].contribute_to_class(model, field_name)
90 |
91 | from categories import settings
92 | from django.core.exceptions import ImproperlyConfigured
93 | from django.db.models import get_model
94 |
95 | for key, value in settings.FK_REGISTRY.items():
96 | model = get_model(*key.split('.'))
97 | if model is None:
98 | raise ImproperlyConfigured('%s is not a model' % key)
99 | if isinstance(value, (tuple, list)):
100 | for item in value:
101 | if isinstance(item, basestring):
102 | register_fk(model, item)
103 | elif isinstance(item, dict):
104 | field_name = item.pop('name')
105 | register_fk(model, field_name, extra_params=item)
106 | else:
107 | raise ImproperlyConfigured("CATEGORY_SETTINGS['FK_REGISTRY'] doesn't recognize the value of %s" % key)
108 | elif isinstance(value, basestring):
109 | register_fk(model, value)
110 | elif isinstance(item, dict):
111 | field_name = item.pop('name')
112 | register_fk(model, field_name, extra_params=item)
113 | else:
114 | raise ImproperlyConfigured("CATEGORY_SETTINGS['FK_REGISTRY'] doesn't recognize the value of %s" % key)
115 | for key, value in settings.M2M_REGISTRY.items():
116 | model = get_model(*key.split('.'))
117 | if model is None:
118 | raise ImproperlyConfigured('%s is not a model' % key)
119 | if isinstance(value, (tuple, list)):
120 | for item in value:
121 | if isinstance(item, basestring):
122 | register_m2m(model, item)
123 | elif isinstance(item, dict):
124 | field_name = item.pop('name')
125 | register_m2m(model, field_name, extra_params=item)
126 | else:
127 | raise ImproperlyConfigured("CATEGORY_SETTINGS['M2M_REGISTRY'] doesn't recognize the value of %s: %s" % (key, item))
128 | elif isinstance(value, basestring):
129 | register_m2m(model, value)
130 | elif isinstance(value, dict):
131 | field_name = value.pop('name')
132 | register_m2m(model, field_name, extra_params=value)
133 | else:
134 | raise ImproperlyConfigured("CATEGORY_SETTINGS['M2M_REGISTRY'] doesn't recognize the value of %s" % key)
135 |
--------------------------------------------------------------------------------
/decorator_apps/lazy_manager_insertion.rst:
--------------------------------------------------------------------------------
1 | ======================
2 | Lazy Manager Insertion
3 | ======================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | Loosely based on Django-mptt. It is very similar to how we handled inserting a field.
9 | ::
10 |
11 | COOLAPP_MODELS = {
12 | 'app1.Model': 'cool_manager',
13 | 'app2.Model': 'cool_manager',
14 | }
15 |
16 |
17 | At the bottom of this app’s models.py, you can read the configuration from settings.
18 |
19 | loop through them and Do a bit of error checking
20 |
21 | Load the model class
22 |
23 | Loop through the given fields
24 |
25 | We make sure that the model doesn’t have an attribute by the same name, we add the field to the model by instantiating the manager and calling its contribute_to_class method.
26 |
27 |
28 | ::
29 |
30 | from django.db.models import get_model
31 | import django.conf import settings
32 | from coolapp.managers import CustomManager
33 |
34 | MODELS = getattr(settings, 'COOLAPP_MODELS', {})
35 |
36 | for model_name, mgr_name in MODELS.items():
37 | if not isinstance(model_name, basestring):
38 | continue
39 |
40 | model = get_model(*model_name.split('.'))
41 |
42 | if not getattr(model, mgr_name, False):
43 | manager = CustomManager()
44 | manager.contribute_to_class(model, mgr_name)
--------------------------------------------------------------------------------
/development_philosophy.rst:
--------------------------------------------------------------------------------
1 | =========================================
2 | My Development Assumptions and Principles
3 | =========================================
4 |
5 | by Corey Oordt
6 |
7 | Since I started developing in Django in 2006, I've been lucky enough to meet and work with many talented people with a variety of experiences. Gradually, typically through failure or dire need, we developed a methodology and approach to development of projects.
8 |
9 | The approach was internalized; each of us *knew* how it worked so it was never directly expressed. With a recent move to a new job, I was struck by the differences and needed to express the ideas in terms of **assumptions** and **principles**.
10 |
11 | *Assumptions* are the preconceptions you and your team hold when approaching a project. These assumptions aren't necessarily bad. As long as you are aware of them, and regularly check to make sure they are valid, they will be helpful.
12 |
13 | *Principles* are the guides to behavior. They are not hard and fast rules that you must never break, merely guides to success that should be understood and deviated from with full knowledge of why it makes sense in the situation.
14 |
15 | Key Design Assumptions
16 | ======================
17 |
18 | .. _no-two-projects-are-alike:
19 |
20 | No two projects are alike.
21 | **************************
22 |
23 | Each project will share some things with others, but not all projects will share the same things, or in the same way. You will need to listen to the needs of the end users and the people running the project.
24 |
25 | Most projects will fail
26 | ***********************
27 |
28 | It should fail quickly, with as little effort as possible to determine its imminent doom. This is not a pessimism, but innovation. The easier it is to try something to see if it works, the more you will try and the more that will work.
29 |
30 | People have their own way of doing things.
31 | ******************************************
32 |
33 | I'm an opinionated bastard. As an opinionated bastard, it really torques me when others make me do things in ways I disagree with. When there are several ways to get the same result, let others get there by any of those means.
34 |
35 | Another way to look at this is to manage *outcomes,* not practices or methods.
36 |
37 | Two or more project's requirements may conflict.
38 | ************************************************
39 |
40 | The conflicts only matter if the projects must share something or are co-dependent.
41 |
42 |
43 | Things change.
44 | **************
45 |
46 | Life moves very fast on the internet. Projects must adapt quickly or fail miserably.
47 |
48 |
49 | Key Design Principles
50 | =====================
51 |
52 | Design the user's experience first
53 | **********************************
54 |
55 | Many developers create solutions that are functional, but not usable. When you start with the user's experience, you at least get more usable solutions, if not better ones.
56 |
57 | Break down the tools to their simplest bits.
58 | ********************************************
59 |
60 | It is easier to find new uses for simple tools than complex tools. I am constantly surprised how people have found new ways to use code that was meant for something else.
61 |
62 | Similar ideas are:
63 |
64 | * Separate functionality whenever possible.
65 |
66 | * Be flexible in implementation.
67 |
68 | * Couple loosely.
69 |
70 | Code is like a making a baby: only do it if you are willing to support it for the rest of your life.
71 | ****************************************************************************************************
72 |
73 | I've learned the hard way that code you wrote years ago can come back to haunt you. I've received calls from people I wrote code for years before asking for help due to an unforeseen bug. (To me the unforeseen bug was that they were still using the software.) Unless you are willing to be a jerk, you got to give them a hand.
74 |
75 | This leads to two practices: use external code libraries whenever possible to reduce the amount of code for which you are directly responsible, and write your code in a way that you won't be too horrified to see it in three years.
76 |
77 | A similar practice is when making a utility to enhance functionality, don't assume that the implementer will "own the code".
78 |
79 | External dependencies should be declared and few
80 | ************************************************
81 |
82 | I see dependencies like farts on an elevator: the fewer the better (and please confess).
83 |
84 | If you want people to do something, make it incredibly easy to do.
85 | ******************************************************************
86 |
87 | And don't forget its sibling: *If you want people to do something a specific way, make it easier to do it that way than any other.*
88 |
89 | Even small barriers will limit how often "best practices" are followed. People put off things if:
90 |
91 | * They have to do prep work to accomplish the task
92 |
93 | * They aren't sure how to accomplish the task
94 |
95 | * They don't understand why the task is important
96 |
97 |
98 |
--------------------------------------------------------------------------------
/example_pattern.rst:
--------------------------------------------------------------------------------
1 | :orphan:
2 |
3 | ============
4 | Pattern Name
5 | ============
6 |
7 | What problem does this pattern solve?
8 | =====================================
9 |
10 | When to use it
11 | ==============
12 |
13 | Why should I use it?
14 | ====================
15 |
16 | Implementation
17 | ==============
18 |
19 | How to use it
20 | =============
21 |
22 | Sources
23 | =======
24 |
25 | Useful Links
26 | ============
27 |
28 | Where is it used?
29 | =================
30 |
31 |
--------------------------------------------------------------------------------
/index.rst:
--------------------------------------------------------------------------------
1 | ======================
2 | Django Coding Patterns
3 | ======================
4 |
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 | :glob:
9 |
10 | what_are_pluggable_apps
11 | development_philosophy
12 | app_construction/index
13 | configuration/index
14 | decorator_apps/index
15 | models/index
16 | templates/index
17 | urls/index
18 |
19 |
20 |
--------------------------------------------------------------------------------
/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | set SPHINXBUILD=sphinx-build
6 | set BUILDDIR=_build
7 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
8 | if NOT "%PAPER%" == "" (
9 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
10 | )
11 |
12 | if "%1" == "" goto help
13 |
14 | if "%1" == "help" (
15 | :help
16 | echo.Please use `make ^` where ^ is one of
17 | echo. html to make standalone HTML files
18 | echo. dirhtml to make HTML files named index.html in directories
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. qthelp to make HTML files and a qthelp project
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 | echo. doctest to run all doctests embedded in the documentation if enabled
27 | goto end
28 | )
29 |
30 | if "%1" == "clean" (
31 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
32 | del /q /s %BUILDDIR%\*
33 | goto end
34 | )
35 |
36 | if "%1" == "html" (
37 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
38 | echo.
39 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
40 | goto end
41 | )
42 |
43 | if "%1" == "dirhtml" (
44 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
45 | echo.
46 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
47 | goto end
48 | )
49 |
50 | if "%1" == "pickle" (
51 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
52 | echo.
53 | echo.Build finished; now you can process the pickle files.
54 | goto end
55 | )
56 |
57 | if "%1" == "json" (
58 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
59 | echo.
60 | echo.Build finished; now you can process the JSON files.
61 | goto end
62 | )
63 |
64 | if "%1" == "htmlhelp" (
65 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
66 | echo.
67 | echo.Build finished; now you can run HTML Help Workshop with the ^
68 | .hhp project file in %BUILDDIR%/htmlhelp.
69 | goto end
70 | )
71 |
72 | if "%1" == "qthelp" (
73 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
74 | echo.
75 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
76 | .qhcp project file in %BUILDDIR%/qthelp, like this:
77 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\app.qhcp
78 | echo.To view the help file:
79 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\app.ghc
80 | goto end
81 | )
82 |
83 | if "%1" == "latex" (
84 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
85 | echo.
86 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
87 | goto end
88 | )
89 |
90 | if "%1" == "changes" (
91 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
92 | echo.
93 | echo.The overview file is in %BUILDDIR%/changes.
94 | goto end
95 | )
96 |
97 | if "%1" == "linkcheck" (
98 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
99 | echo.
100 | echo.Link check complete; look for any errors in the above output ^
101 | or in %BUILDDIR%/linkcheck/output.txt.
102 | goto end
103 | )
104 |
105 | if "%1" == "doctest" (
106 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
107 | echo.
108 | echo.Testing of doctests in the sources finished, look at the ^
109 | results in %BUILDDIR%/doctest/output.txt.
110 | goto end
111 | )
112 |
113 | :end
114 |
--------------------------------------------------------------------------------
/models/abstract_model_mixins.rst:
--------------------------------------------------------------------------------
1 | =====================
2 | Abstract Model Mixins
3 | =====================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | What problem does this pattern solve?
9 | =====================================
10 |
11 | It creates a tool kit to build complex models.
12 |
13 | When to use it
14 | ==============
15 |
16 | For models that you will commonly build in projects but have many different potential features, and you want your models to only contain the features necessary. Blogs are a good example, where where are many potential options to include within a blog, but you don't need all of them all the time.
17 |
18 | Why should I use it?
19 | ====================
20 |
21 | This allows developers to fix bugs once, in the tool kit. Installing the new tool kit version will fix those bugs in each model created from it.
22 |
23 | Where is it used?
24 | =================
25 |
26 | I first saw this in `GLAMkit `_\ 's `blogtools `_ package. It is also used in Django 1.3's `class-based views `_\ .
27 |
28 | Implementation
29 | ==============
30 |
31 | Models
32 | ------
33 |
34 | First isolate all the potential or optional features from core features. The core features and each isolated set of optional features will make up individual abstract Django models.
35 |
36 | GLAMkit isolated the one base model (``EntryBase``\ ), and four optional features: add a *featured* flag, add a *status* field, add a tag field, and allow for the body and excerpt content to convert to HTML.
37 |
38 | ``EntryBase`` includes seven fields--author, title, pub_date, slug, enable_comments, excerpt, and body--as well as ``__unicode__()``\ , ``get_absolute_url()`` and ``excerpt_or_body()`` functions. The ``Meta`` class has ``abstract=True`` so that Django never tries to represent this model in a database. It must be subclassed.
39 |
40 | ``FeaturableEntryMixin`` is an abstract class that merely defines an ``is_featured`` field.
41 |
42 | ``StatusableEntryMixin`` is an abstract class that defines ``LIVE_STATUS``\ , ``DRAFT_STATUS``\ , and ``HIDDEN_STATUS`` values and choices. It defines a ``status`` field with those choices.
43 |
44 | ``TaggableEntryMixin`` is an abstract class that is only available if Django-Tagging is installed, as it uses the ``TagField`` for the ``tags`` field it defines.
45 |
46 | ``HTMLFormattableEntryMixin`` is a much more complex abstract class. It is only available if the ``template_utils`` package is available. It defines two text fields, ``excerpt_html`` and ``body_html``\ . It also overrides the ``save()`` method so it can convert the contents of ``exceprt`` and ``body`` into HTML for ``excerpt_html`` and ``body_html``\ , respectively. Finally it re-defines the ``excerpt_or_body()`` method to return the ``excerpt_html`` or ``body_html`` value.
47 |
48 | Admin
49 | -----
50 |
51 | It is difficult to provide a really good ``ModelAdmin`` class when you aren't sure what fields or features are included in the final model. GLAMkit provides a ``EntryAdminBase`` which is subclassed from ``object`` (not ``ModelAdmin``\ ). Providing other admin mixins would make sense if there were admin-specific features to provide, such as adding a WYSIWYG editor, autocomplete lookups or special filtering.
52 |
53 | URLs
54 | ----
55 |
56 |
57 | Views
58 | -----
59 |
60 |
61 | Syndication Feeds
62 | -----------------
63 |
64 |
65 |
66 |
67 | How to use it
68 | =============
69 |
70 | Sources
71 | =======
72 |
73 | Useful Links
74 | ============
75 |
76 |
77 | GLAMKit (Gallery, Library, Archive, Museum) an Australian group, tackles this situation with a set of abstract classes that provide very basic features.
78 |
79 | You create your model by subclassing the classes that provide the functionality you need.
80 |
81 | And you don’t have to stop there. You can add your own fields as well.
82 |
83 |
84 | .. code-block:: python
85 |
86 | class PRBlog(EntryBase,
87 | StatusableEntryMixin):
88 |
89 | subhead = models.CharField()
90 | pdf = models.FileField()
91 |
92 |
93 |
--------------------------------------------------------------------------------
/models/automatically_filling_user.rst:
--------------------------------------------------------------------------------
1 | ============================================
2 | Automatically Filling in a User in the Admin
3 | ============================================
4 |
5 | What problem does this pattern solve?
6 | =====================================
7 |
8 | * Easily keep track of the last user who modified a record
9 | * Automatically set the "author" of a record
10 |
11 | Why should I use it?
12 | ====================
13 |
14 | It is a safe way to save the user time and still keep track of the information.
15 |
16 | Implementation
17 | ==============
18 |
19 | The important parts of the implementation are:
20 |
21 | 1. The :py:class:`ForeignKey` to :py:class:`User` field needs ``blank=True``
22 | 2. Create a :py:class:`ModelForm` that assigns a fake :py:class:`User` to the field.
23 | 3. Override the :py:func:`ModelAdmin.save_model()` function to assign the ``request.user`` to the field.
24 |
25 |
26 | For discussion we'll use this model with two relations to the :py:class:`User` model:
27 |
28 | .. rst-class:: caption
29 |
30 | **coolblog/models.py**
31 |
32 | .. literalinclude:: automatically_filling_user_model.py
33 | :linenos:
34 |
35 | This :py:class:`Entry` model has two fields that we want to fill automatically: ``author`` and ``last_modified_by``\ . Notice both fields have ``blank=True``\ . This is important so we can get past some initial Django validation.
36 |
37 | Faking validation
38 | -----------------
39 |
40 | Whenever you save a model, Django attempts to validate it. Validation will fail without special validation tomfoolery. The first stage of validation fakery was setting ``blank=True`` on the fields. The next stage involves setting a temporary value for each field.
41 |
42 | .. rst-class:: caption
43 |
44 | **coolblog/forms.py**
45 |
46 | .. literalinclude:: automatically_filling_user_form.py
47 | :linenos:
48 |
49 |
50 | The lower-level Django model validation actually checks if the value of a related field is an instance of the correct class. Since a form's validation happens before any attempt to save the model, we create a new :py:class:`ModelForm`, called ``EntryForm``\ . The :py:func:`clean_author()` and :py:func:`clean_last_modified_by()` methods check for an empty value and assigns it an unsaved and empty instance of :py:class:`User`\ , and Django is happy.
51 |
52 |
53 | Saving the model
54 | ----------------
55 |
56 | In the model's :py:class:`ModelAdmin`\ , we make a few adjustments.
57 |
58 | .. rst-class:: caption
59 |
60 | **coolblog/admin.py**
61 |
62 | .. literalinclude:: automatically_filling_user_admin.py
63 | :linenos:
64 |
65 | First, we set the ``form`` attribute to the ``EntryForm`` we just created.
66 |
67 | Then, since we don't want the author to worry about selecting themselves in the ``author`` field, we left it out of the ``fieldsets``\ . We left in the ``last_modified_by`` field for reference and made it a read-only field.
68 |
69 | The final magic comes in the overridden :py:func:`save_model()` method on line 17. We check to see if the ``author`` attribute actually has an ``id``\ . If it doesn't, it must be the empty instance we set in ``EntryForm``\ , so we assign the ``author`` field to the current user. Since we always want to assign or re-assign the user modifying this record, the ``last_modified_by`` field is set every time.
70 |
71 |
72 | Sources
73 | =======
74 |
75 | `ModelAdmin.save_model documentation `_
76 |
77 | `Audit Fields `_
78 |
79 | `Users and the admin `_
80 |
--------------------------------------------------------------------------------
/models/automatically_filling_user_admin.py:
--------------------------------------------------------------------------------
1 | class EntryAdmin(admin.ModelAdmin):
2 | form = EntryForm
3 |
4 | list_display = ('title', 'pub_date', 'author')
5 | prepopulated_fields = { 'slug': ['title'] }
6 | readonly_fields = ('last_modified', 'last_modified_by',)
7 | fieldsets = ((
8 | None, {
9 | 'fields': ('title', 'body', 'pub_date')
10 | }), (
11 | 'Other Information', {
12 | 'fields': ('last_modified', 'last_modified_by', 'slug'),
13 | 'classes': ('collapse',)
14 | })
15 | )
16 |
17 | def save_model(self, request, obj, form, change):
18 | if not obj.author.id:
19 | obj.author = request.user
20 | obj.last_modified_by = request.user
21 | obj.save()
--------------------------------------------------------------------------------
/models/automatically_filling_user_admin2.py:
--------------------------------------------------------------------------------
1 | class EntryAdmin(admin.ModelAdmin):
2 | form = EntryForm
3 |
4 | list_display = ('title', 'pub_date', 'author')
5 | prepopulated_fields = { 'slug': ['title'] }
6 | readonly_fields = ('last_modified', 'last_modified_by',)
7 | fieldsets = ((
8 | None, {
9 | 'fields': ('title', 'body', 'pub_date')
10 | }), (
11 | 'Other Information', {
12 | 'fields': ('author', 'last_modified', 'last_modified_by', 'slug'),
13 | 'classes': ('collapse',)
14 | })
15 | )
16 |
17 | def save_model(self, request, obj, form, change):
18 | if not obj.author.id:
19 | obj.author = request.user
20 | obj.last_modified_by = request.user
21 | obj.save()
--------------------------------------------------------------------------------
/models/automatically_filling_user_form.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 |
3 | class EntryForm(forms.ModelForm):
4 | class Meta:
5 | model = Entry
6 |
7 | def clean_author(self):
8 | if not self.cleaned_data['author']:
9 | return User()
10 | return self.cleaned_data['author']
11 |
12 | def clean_last_modified_by(self):
13 | if not self.cleaned_data['last_modified_by']:
14 | return User()
15 | return self.cleaned_data['last_modified_by']
--------------------------------------------------------------------------------
/models/automatically_filling_user_model.py:
--------------------------------------------------------------------------------
1 | class Entry(models.Model):
2 | title = models.CharField(max_length=250)
3 | slug = models.SlugField()
4 | pub_date = models.DateField(default=datetime.datetime.today)
5 | author = models.ForeignKey(
6 | User,
7 | related_name='entries',
8 | blank=True)
9 | body = models.TextField()
10 | last_modified = models.DateTimeField(auto_now=True)
11 | last_modified_by = models.ForeignKey(
12 | User,
13 | related_name='entry_modifiers',
14 | blank=True)
--------------------------------------------------------------------------------
/models/common_options.rst:
--------------------------------------------------------------------------------
1 | ==========================================
2 | Configurable Options for Common Situations
3 | ==========================================
4 | .. warning::
5 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
6 |
7 | A few, well-known of variations
8 | (e.g. Use django.contrib.sites?)
9 |
10 | **models.py**
11 | .. literalinclude:: common_options_1.py
12 | :linenos:
13 |
14 |
15 | Another example:
16 |
17 |
18 | **models.py**
19 | .. literalinclude:: common_options_2.py
20 | :linenos:
21 |
22 |
23 | You can provide for optional field settings.
24 |
25 | Import the setting from your own apps settings
26 |
27 | Based on that setting, you can optionally import classes. And in your model definition...
28 |
29 | Optionally declare fields. The only drawback of this depends on the type of field. Changing your mind after the initial table creation might require you to either manually add the field or drop the table and syncdb again.
30 |
31 | Link to way to do migration with south if field is added.
--------------------------------------------------------------------------------
/models/common_options_1.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from coolapp.settings import MULTIPLE_SITES, SINGLE_SITE
3 |
4 | if MULTIPLE_SITES or SINGLE_SITE:
5 | from django.contrib.sites.models import Site
6 |
7 | class Entry(models.Model):
8 | title = models.CharField(max_length=100)
9 | # Other stuff
10 |
11 | if MULTIPLE_SITES:
12 | sites = models.ManyToManyField(Site)
13 | if SINGLE_SITE:
14 | sites = models.ForeignKey(Site)
15 |
--------------------------------------------------------------------------------
/models/common_options_2.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from myapp.settings import USE_TAGGING
3 |
4 | if USE_TAGGING:
5 | from tagging.fields import TagField
6 |
7 | class Entry(models.Model):
8 | title = models.CharField(max_length=100)
9 | # Other stuff
10 |
11 | if USE_TAGGING:
12 | tags = TagField()
13 |
--------------------------------------------------------------------------------
/models/configurable_relations.rst:
--------------------------------------------------------------------------------
1 | =========================
2 | Configurable Foreign Keys
3 | =========================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | We had a staff model that we wanted to related it to in some projects, but not all. So in the application settings we use a django function called get_model. This allows you to specify the model in an app-dot-model format in the project settings and then dynamically import it
9 |
10 | .. code-block:: python
11 |
12 | from django.conf import settings
13 | from django.db.models import get_model
14 |
15 | model_string = getattr(settings, 'VIEWPOINT_AUTHOR_MODEL', 'auth.User')
16 | AUTHOR_MODEL = get_model(*model_string.split('.'))
17 |
18 | Now we simply import the AUTHOR_MODEL setting, which is a django model. And use it as the parameter for the ForeignKey field.
19 |
20 | .. code-block:: python
21 |
22 | from viewpoint.settings import AUTHOR_MODEL
23 |
24 | class Entry(models.Model):
25 | title = models.CharField(max_length=100)
26 | author = models.ForeignKey(AUTHOR_MODEL)
27 | ...
--------------------------------------------------------------------------------
/models/flexible_storage_uploaded_files.rst:
--------------------------------------------------------------------------------
1 | ==================================
2 | Flexible storage of uploaded files
3 | ==================================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | ::
9 |
10 | from django.conf import settings
11 | from django.core.files.storage import get_storage_class
12 |
13 | DEFAULT_STORAGE = get_storage_class(
14 | getattr(settings, "MMEDIA_DEFAULT_STORAGE", settings.DEFAULT_FILE_STORAGE)
15 | )
16 |
17 |
18 | ::
19 |
20 | from massmedia.settings import IMAGE_UPLOAD_TO, DEFAULT_STORAGE
21 |
22 | class Image(models.Model):
23 | file = models.FileField(
24 | upload_to = IMAGE_UPLOAD_TO,
25 | blank = True,
26 | null = True,
27 | storage=DEFAULT_STORAGE())
--------------------------------------------------------------------------------
/models/index.rst:
--------------------------------------------------------------------------------
1 | ==============
2 | Model Patterns
3 | ==============
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 | :glob:
8 |
9 | abstract_model_mixins
10 | automatically_filling_user
11 | common_options
12 | configurable_relations
13 | flexible_storage_uploaded_files
--------------------------------------------------------------------------------
/templates/easily_overridable_templates.rst:
--------------------------------------------------------------------------------
1 | ============================
2 | Easily overridable templates
3 | ============================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | Templates are an important part of your pluggable app. They demonstrate how your app works. The more complex your app, the more important templates are. Following a few practices can make your templates very useful. We have two goals: 1. Get demonstrable functionality in as short a time as possible, and 2. modify the fewest templates to do so.
9 |
10 | Instead of putting your templates loose in your templates directory where they can conflict with other apps templates, *** put them in a directory within templates, named after your app to name space them. *** Then you reference them as the relative path from templates, in this case: coolapp/base.html
11 |
12 | ::
13 |
14 | coolapp
15 | +-templates
16 | +-coolapp
17 | +-base.html
18 |
19 |
--------------------------------------------------------------------------------
/templates/extend_one_template.rst:
--------------------------------------------------------------------------------
1 | ===================
2 | Extend one template
3 | ===================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | If all your templates extend a template that you assume exists, such as ``base.html``
9 |
10 | You have to change each template when your project uses ``site_base.html`` instead.
11 |
12 | If instead all your templates extend a known, base template in your name space and it extends the mythical ``base.html``
13 |
14 | when the inevitable happens and the base template name changes changing one template makes all the others work.
15 |
16 | If your coolapp/base.html defines all the blocks that you use, it is also trivial to change them to match the project’s template, just by enclosing your blocks in the appropriate base template blocks
17 |
18 |
19 | ``coolapp/base.html``
20 |
21 | ::
22 |
23 | {% extends "site_base.html" %}
24 |
25 | {% block extra_head %}
26 | {% block head %}
27 | {% endblock %}
28 | {% endblock %}
29 |
30 | {% block content %}
31 | {% block body %}
32 | {% endblock %}
33 | {% endblock %}
34 |
35 |
--------------------------------------------------------------------------------
/templates/import_your_template_blocks.rst:
--------------------------------------------------------------------------------
1 | ===========================
2 | Import your template blocks
3 | ===========================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | If each template only focuses on one template block and imports other blocks, such as extra header information, you can selectively choose which template to modify: extra_head.html to add this functionality once for all templates, or detail.html to change the content.
9 |
10 |
11 | ::
12 |
13 | {% extends "coolapp/base.html" %}
14 |
15 | {% block extra_head %}
16 | {{ block.super }}
17 | {% import "coolapp/extra_head.html" %}
18 | {% endblock %}
19 |
20 | {% block content %}
21 | {# Important content stuff here #}
22 |
23 |
24 |
25 | {% endblock %}
--------------------------------------------------------------------------------
/templates/index.rst:
--------------------------------------------------------------------------------
1 | =================
2 | Template Patterns
3 | =================
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 | :glob:
8 |
9 | easily_overridable_templates
10 | extend_one_template
11 | import_your_template_blocks
--------------------------------------------------------------------------------
/urls/index.rst:
--------------------------------------------------------------------------------
1 | ============
2 | URL patterns
3 | ============
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 | :glob:
8 |
9 | urls_that_live_under_any_prefix
--------------------------------------------------------------------------------
/urls/urls_that_live_under_any_prefix.rst:
--------------------------------------------------------------------------------
1 | ===============================
2 | URLs that live under any prefix
3 | ===============================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | It’s bad practice to hard code url paths or assume certain paths, for example that my blog app is always going to be under the path /blogs/
9 |
10 | Django provides an easy way to abstractly reference a url and its view. All you have to do ...
11 |
12 | Is add a url function in front of the pattern and add a name parameter. This allows you to ...
13 |
14 | ::
15 |
16 | from django.conf.urls.defaults import *
17 |
18 | urlpatterns = patterns('',
19 | url(r'^$', 'coolapp_app.views.index', name='coolapp_index'),
20 | )
21 |
22 |
23 | ::
24 |
25 |
26 |
27 |
28 | Retrieve the url using the url template tag or use the reverse function within your code.
29 |
30 |
31 | ::
32 |
33 | from django.core.urlresolvers import reverse
34 |
35 | ::
36 |
37 | def myview(request):
38 | return HttpResponseRedirect(reverse('coolapp_index', args=[]))
--------------------------------------------------------------------------------
/what_are_pluggable_apps.rst:
--------------------------------------------------------------------------------
1 | ========================
2 | What are pluggable apps?
3 | ========================
4 |
5 | .. warning::
6 | This is just a stub document. It will be fleshed out more. If you wish to comment on it, please e-mail coreyoordt at gmail.
7 |
8 | Confusion between the "web app" the user sees and the Django apps that power it.
9 |
10 | Pluggable apps are:
11 |
12 | * Focused: focused use cases and include nothing that isn’t required. "Write programs that do one thing and do it well." — Doug McIlroy (inventor of UNIX pipes)"
13 |
14 | * Self-contained: Everything someone needs to get the app working is declared or included.
15 |
16 | * Easily adaptable: A focused application can inevitably find new uses, if it doesn’t take too much for granted or make too many assumptions.
17 |
18 | * Easily installed: Pluggable applications are installed and not modified. Applications are wired together and configured in the project. The only “Apps” in your project codebase are apps that are so specific to the project that they can’t be used elsewhere.
19 |
20 |
--------------------------------------------------------------------------------