summaryrefslogtreecommitdiff
path: root/docs/narr/extending.rst
blob: 4162075842f2af556c221e9ff84a8843800d73e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
.. _extending_chapter:

Extending An Existing :app:`Pyramid` Application
===================================================

If a :app:`Pyramid` developer has obeyed certain constraints while building
an application, a third party should be able to change the application's
behavior without needing to modify its source code.  The behavior of a
:app:`Pyramid` application that obeys certain constraints can be *overridden*
or *extended* without modification.

We'll define some jargon here for the benefit of identifying the parties
involved in such an effort.

Developer
  The original application developer.

Integrator
  Another developer who wishes to reuse the application written by the
  original application developer in an unanticipated context.  He may also
  wish to modify the original application without changing the original
  application's source code.

The Difference Between "Extensible" and "Pluggable" Applications
----------------------------------------------------------------

Other web frameworks, such as :term:`Django`, advertise that they allow
developers to create "pluggable applications".  They claim that if you create
an application in a certain way, it will be integratable in a sensible,
structured way into another arbitrarily-written application or project
created by a third-party developer.

:app:`Pyramid`, as a platform, does not claim to provide such a feature.  The
platform provides no guarantee that you can create an application and package
it up such that an arbitrary integrator can use it as a subcomponent in a
larger Pyramid application or project.  Pyramid does not mandate the
constraints necessary for such a pattern to work satisfactorily.  Because
Pyramid is not very "opinionated", developers are able to use wildly
different patterns and technologies to build an application.  A given Pyramid
application may happen to be reusable by a particular third party integrator,
because the integrator and the original developer may share similar base
technology choices (such as the use of a particular relational database or
ORM).  But the same application may not be reusable by a different developer,
because he has made different technology choices which are incompatible with
the original developer's.

As a result, the concept of a "pluggable application" is left to layers built
above Pyramid, such as a "CMS" layer or "application server" layer.  Such
layers are apt to provide the necessary "opinions" (such as mandating a
storage layer, a templating system, and a structured, well-documented pattern
of registering that certain URLs map to certain bits of code) which makes the
concept of a "pluggable application" possible.  "Pluggable applications",
thus, should not plug in to Pyramid itself but should instead plug into a
system written atop Pyramid.

Although it does not provide for "pluggable applications", Pyramid *does*
provide a rich set of mechanisms which allows for the extension of a single
existing application.  Such features can be used by frameworks built using
Pyramid as a base.  All Pyramid applications may not be *pluggable*, but all
Pyramid applications are *extensible*.

.. index::
   single: extensible application

Rules for Building An Extensible Application
--------------------------------------------

There is only one rule you need to obey if you want to build a maximally
extensible :app:`Pyramid` application: as a developer, you should factor any
overrideable :term:`imperative configuration` you've created into functions
which can be used via :meth:`pyramid.config.Configurator.include` rather than
inlined as calls to methods of a :term:`Configurator` within the ``main``
function in your application's ``__init__.py``.  For example, rather than:

.. code-block:: python
   :linenos:

   from pyramid.config import Configurator

   if __name__ == '__main__':
       config = Configurator()
       config.add_view('myapp.views.view1', name='view1')
       config.add_view('myapp.views.view2', name='view2')

You should do move the calls to ``add_view`` outside of the (non-reusable)
``if __name__ == '__main__'`` block, and into a reusable function:

.. code-block:: python
   :linenos:

   from pyramid.config import Configurator

   if __name__ == '__main__':
       config = Configurator()
       config.include(add_views)

   def add_views(config):
       config.add_view('myapp.views.view1', name='view1')
       config.add_view('myapp.views.view2', name='view2')

Doing this allows an integrator to maximally reuse the configuration
statements that relate to your application by allowing him to selectively
include or disinclude the configuration functions you've created from an
"override package".

Alternately, you can use :term:`ZCML` for the purpose of making configuration
extensible and overrideable. :term:`ZCML` declarations that belong to an
application can be overridden and extended by integrators as necessary in a
similar fashion.  If you use only :term:`ZCML` to configure your application,
it will automatically be maximally extensible without any manual effort.  See
:term:`pyramid_zcml` for information about using ZCML.

Fundamental Plugpoints
~~~~~~~~~~~~~~~~~~~~~~

The fundamental "plug points" of an application developed using
:app:`Pyramid` are *routes*, *views*, and *assets*.  Routes are declarations
made using the :meth:`pyramid.config.Configurator.add_route` method.  Views
are declarations made using the :meth:`pyramid.config.Configurator.add_view`
method.  Assets are files that are
accessed by :app:`Pyramid` using the :term:`pkg_resources` API such as static
files and templates via a :term:`asset specification`.  Other directives and
configurator methods also deal in routes, views, and assets.  For example,
``add_handler`` directive of the ``pyramid_handlers`` package adds a single
route, and some number of views.

.. index::
   single: extending an existing application

Extending an Existing Application
---------------------------------

The steps for extending an existing application depend largely on whether the
application does or does not use configuration decorators and/or imperative
code.

If The Application Has Configuration Decorations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You've inherited a :app:`Pyramid` application which you'd like to extend or
override that uses :class:`pyramid.view.view_config` decorators or other
:term:`configuration decoration` decorators.

If you just want to *extend* the application, you can run a :term:`scan`
against the application's package, then add additional configuration that
registers more views or routes.

.. code-block:: python
   :linenos:

   if __name__ == '__main__':
       config.scan('someotherpackage')
       config.add_view('mypackage.views.myview', name='myview')

If you want to *override* configuration in the application, you *may* need to
run :meth:`pyramid.config.Configurator.commit` after performing the scan of
the original package, then add additional configuration that registers more
views or routes which performs overrides.

.. code-block:: python
   :linenos:

   if __name__ == '__main__':
       config.scan('someotherpackage')
       config.commit()
       config.add_view('mypackage.views.myview', name='myview'

Once this is done, you should be able to extend or override the application
like any other (see :ref:`extending_the_application`).

You can alternately just prevent a :term:`scan` from happening (by omitting
any call to the :meth:`pyramid.config.Configurator.scan` method).  This will
cause the decorators attached to objects in the target application to do
nothing.  At this point, you will need to convert all the configuration done
in decorators into equivalent imperative configuration or ZCML and add that
configuration or ZCML to a separate Python package as described in
:ref:`extending_the_application`.

.. _extending_the_application:

Extending the Application
~~~~~~~~~~~~~~~~~~~~~~~~~

To extend or override the behavior of an existing application, you will need
to create a new package which includes the configuration of the old package,
and you'll perhaps need to create implementations of the types of things
you'd like to override (such as views), which are referred to within the
original package.

The general pattern for extending an existing application looks something
like this:

- Create a new Python package.  The easiest way to do this is to create a new
  :app:`Pyramid` application using the "paster" template mechanism.  See
  :ref:`creating_a_project` for more information.

- In the new package, create Python files containing views and other
  overridden elements, such as templates and static assets as necessary.

- Install the new package into the same Python environment as the original
  application (e.g. ``python setup.py develop`` or ``python setup.py
  install``).

- Change the ``main`` function in the new package's ``__init__py`` to include
  the original :app:`Pyramid` application's configuration functions via
  :meth:`pyramid.config.Configurator.include` statements or a :term:`scan`.

- Wire the new views and assets created in the new package up using
  imperative registrations within the ``main`` function of the
  ``__init__.py`` file of the new application.  These wiring should happen
  *after* including the configuration functions of the old application.
  These registrations will extend or override any registrations performed by
  the original application.  See :ref:`overriding_views`,
  :ref:`overriding_routes` and :ref:`overriding_resources`.

.. index::
   pair: overriding; views

.. _overriding_views:

Overriding Views
~~~~~~~~~~~~~~~~~

The :term:`view configuration` declarations you make which *override*
application behavior will usually have the same :term:`view predicate`
attributes as the original you wish to override.  These ``<view>``
declarations will point at "new" view code, in the override package you've
created.  The new view code itself will usually be cut-n-paste copies of view
callables from the original application with slight tweaks.

For example, if the original application has the following
``configure_views`` configuration method:

.. code-block:: python
   :linenos:

    def configure_views(config):
        config.add_view('theoriginalapp.views.theview', name='theview')

You can override the first view configuration statement made by
``configure_views`` within the override package, after loading the original
configuration function:

.. code-block:: python
   :linenos:

   from pyramid.config import Configurator
   from originalapp import configure_views

   if __name == '__main__':
       config = Configurator()
       config.include(configure_views)
       config.add_view('theoverrideapp.views.theview', name='theview')

In this case, the ``theoriginalapp.views.theview`` view will never be
executed.  Instead, a new view, ``theoverrideapp.views.theview`` will be
executed instead, when request circumstances dictate.

A similar pattern can be used to *extend* the application with ``add_view``
declarations.  Just register a new view against some other set of predicates
to make sure the URLs it implies are available on some other page rendering.

.. index::
   pair: overriding; routes

.. _overriding_routes:

Overriding Routes
~~~~~~~~~~~~~~~~~

Route setup is currently typically performed in a sequence of ordered calls
to :meth:`~pyramid.config.Configurator.add_route`.  Because these calls are
ordered relative to each other, and because this ordering is typically
important, you should retain their relative ordering when performing an
override.  Typically, this means *copying* all the ``add_route`` statements
into the override package's file and changing them as necessary.  Then
disinclude any ``add_route`` statements from the original application.

.. index::
   pair: overriding; assets

.. _overriding_resources:

Overriding Assets
~~~~~~~~~~~~~~~~~

Assets are files on the filesystem that are accessible within a Python
*package*.  An entire chapter is devoted to assets: :ref:`assets_chapter`.
Within this chapter is a section named :ref:`overriding_assets_section`.
This section of that chapter describes in detail how to override package
assets with other assets by using the
:meth:`pyramid.config.Configurator.override_asset` method.  Add such
``override_asset`` calls to your override package's ``__init__.py`` to
perform overrides.