summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcewing <cris@crisewing.com>2017-05-22 16:38:53 -0700
committercewing <cris@crisewing.com>2017-05-22 16:38:53 -0700
commit52ff50f75d265a4c7d04db538c26b61ab05677c6 (patch)
tree504a72cfb759e5269c35b65edae91cdab60dfc96
parentf47d226dc4c390a476f2b1fabe8e56d458c971ae (diff)
downloadpyramid-52ff50f75d265a4c7d04db538c26b61ab05677c6.tar.gz
pyramid-52ff50f75d265a4c7d04db538c26b61ab05677c6.tar.bz2
pyramid-52ff50f75d265a4c7d04db538c26b61ab05677c6.zip
Updating wording in the advanced features doc to make it more accessible
-rw-r--r--docs/narr/advfeatures.rst352
1 files changed, 179 insertions, 173 deletions
diff --git a/docs/narr/advfeatures.rst b/docs/narr/advfeatures.rst
index 4365b1855..5c45cf61a 100644
--- a/docs/narr/advfeatures.rst
+++ b/docs/narr/advfeatures.rst
@@ -5,104 +5,123 @@ Pyramid has been built from the ground up to avoid the problems that other
frameworks can suffer.
-No singletons
-~~~~~~~~~~~~~
-
-Pyramid is written in such a way that it requires your application to have
-exactly zero "singleton" data structures. Or put another way, Pyramid doesn't
-require you to construct any "mutable globals". Or put even another different
-way, an import of a Pyramid application needn't have any "import-time side
-effects". This is esoteric-sounding, but if you've ever tried to cope with
-parameterizing a Django ``settings.py`` file for multiple installations of the
-same application, or if you've ever needed to monkey-patch some framework
-fixture so that it behaves properly for your use case, or if you've ever wanted
-to deploy your system using an asynchronous server, you'll end up appreciating
-this feature. It just won't be a problem. You can even run multiple copies of
-a similar but not identically configured Pyramid application within the same
-Python process. This is good for shared hosting environments, where RAM is at
-a premium.
-
-View predicates and many views per route
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+You Don't Need Singletons
+-------------------------
+
+Have you ever struggled with parametrizing Django's ``settings.py`` file for
+multiple installations of the same Django application? Have you ever needed to
+monkey-patch a framework fixture to get it to behave properly for your
+use-case? Have you ever tried to deploy your application using an asynchronous
+server and failed?
+
+All these problems are symptoms of "mutable global state", also known as
+"import-time side effects" and arise from the use of "singleton" data structures.
+
+:app:`Pyramid` is written so that you don't run into these types of problems.
+It is even possible to run multiple copies of the *same* :app:`Pyramid`
+application configured differently within a single Python process. This makes
+running Pyramid in shared hosting environments a snap.
+
+Simplify your View Code with Predicates
+---------------------------------------
+
+How many times have you found yourself beginning the logic of your view code
+with something like this::
+
+ if request.user.is_authenticated:
+ # do one thing
+ else:
+ # do something else
Unlike many other systems, Pyramid allows you to associate more than one view
-per route. For example, you can create a route with the pattern ``/items`` and
-when the route is matched, you can shuffle off the request to one view if the
-request method is GET, another view if the request method is POST, etc. A
-system known as "view predicates" allows for this. Request method matching is
-the most basic thing you can do with a view predicate. You can also associate
-views with other request parameters, such as the elements in the query string,
-the Accept header, whether the request is an XHR request or not, and lots of
-other things. This feature allows you to keep your individual views clean.
-They won't need much conditional logic, so they'll be easier to test.
+with a single route. For example, you can create a route with the pattern
+``/items`` and when the route is matched, you can send the request to one view
+if the request method is GET, another view if the request method is POST, and
+so on.
+
+:app:`Pyramid` uses a system of "view predicates" to allow this. Matching the
+request method is one basic thing you can do with a view predicate. You can
+also associate views with other request parameters, such as elements in the
+query string, the Accept header, whether the request is an XHR request or not,
+and lots of other things.
+
+For our example above, you can do this instead::
+
+ @view_config(route_name="items", effective_principals=pyramid.security.Authenticated)
+ def auth_view(request):
+ # do one thing
+
+ @view_config(route_name="items")
+ def anon_view(request):
+ # do something else
+
+This approach allows you to develop view code that is simpler, more easily
+understandable, and more directly testable.
Example: :ref:`view_configuration_parameters`.
-Transaction management
-~~~~~~~~~~~~~~~~~~~~~~
+Stop Worrying About Transactions
+--------------------------------
Pyramid's :term:`scaffold` system renders projects that include a *transaction
-management* system, stolen from Zope. When you use this transaction management
-system, you cease being responsible for committing your data anymore. Instead
-Pyramid takes care of committing: it commits at the end of a request or aborts
-if there's an exception. Why is that a good thing? Having a centralized place
-for transaction management is a great thing. If, instead of managing your
-transactions in a centralized place, you sprinkle ``session.commit`` calls in
-your application logic itself, you can wind up in a bad place. Wherever you
-manually commit data to your database, it's likely that some of your other code
-is going to run *after* your commit. If that code goes on to do other important
-things after that commit, and an error happens in the later code, you can
-easily wind up with inconsistent data if you're not extremely careful. Some
-data will have been written to the database that probably should not have.
-Having a centralized commit point saves you from needing to think about this;
-it's great for lazy people who also care about data integrity. Either the
-request completes successfully, and all changes are committed, or it does not,
-and all changes are aborted.
-
-Pyramid's transaction management system allows you to synchronize commits
-between multiple databases. It also allows you to do things like conditionally
-send email if a transaction commits, but otherwise keep quiet.
+management* system. When you use this system, you can stop worrying about when
+to commit your changes, :app:`Pyramid` handles it for you. The system will
+commit at the end of a request or aborts if there was an exception.
+
+Why is that a good thing? Imagine a situation where you manually commit a
+change to your persistence layer. It's very likely that other framework code
+will run *after* your changes are done. If an error happens in that other code,
+you can easily wind up with inconsistent data if you're not extremely careful.
+
+Using transaction management saves you from needing to think about this. Either
+a request completes successfully, and all changes are committed, or it does
+not, and all changes are aborted.
+
+Pyramid's transaction management is extendable, so you can synchronize commits
+between multiple databases, or databases of different kinds. It also allows you
+to do things like conditionally send email if a transaction commits, but
+otherwise keep quiet.
Example: :ref:`bfg_sql_wiki_tutorial` (note the lack of commit statements
anywhere in application code).
-Configuration conflict detection
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Stop Worrying About Configuration
+---------------------------------
When a system is small, it's reasonably easy to keep it all in your head. But
-when systems grow large, you may have hundreds or thousands of configuration
-statements which add a view, add a route, and so forth.
+as systems grow large, configuration grows more complex. Your app may grow to
+have hundreds or even thousands of configuration statements.
-Pyramid's configuration system keeps track of your configuration statements. If
-you accidentally add two that are identical, or Pyramid can't make sense out of
+Pyramid's configuration system keeps track of your configuration. If you
+accidentally add two that are identical, or Pyramid can't make sense out of
what it would mean to have both statements active at the same time, it will
-complain loudly at startup time. It's not dumb though. It will automatically
-resolve conflicting configuration statements on its own if you use the
-configuration :meth:`~pyramid.config.Configurator.include` system. "More local"
-statements are preferred over "less local" ones. This allows you to
-intelligently factor large systems into smaller ones.
+complain loudly at startup time.
+
+Pyramid's configuration system is not dumb though. If you use the confugration
+:meth:`~pyramid.config.Configurator.include` system, it can automatically
+resolve conflicts on its own. "More local" statements are preferred over "less
+local" ones. So you can intelligently factor large systems into smaller ones.
Example: :ref:`conflict_detection`.
-Configuration extensibility
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Unlike other systems, Pyramid provides a structured "include" mechanism (see
-:meth:`~pyramid.config.Configurator.include`) that allows you to combine
-applications from multiple Python packages. All the configuration statements
-that can be performed in your "main" Pyramid application can also be performed
-by included packages, including the addition of views, routes, subscribers, and
-even authentication and authorization policies. You can even extend or override
-an existing application by including another application's configuration in
-your own, overriding or adding new views and routes to it. This has the
-potential to allow you to create a big application out of many other smaller
-ones. For example, if you want to reuse an existing application that already
-has a bunch of routes, you can just use the ``include`` statement with a
-``route_prefix``. The new application will live within your application at an
-URL prefix. It's not a big deal, and requires little up-front engineering
-effort.
-
-For example:
+Compose Powerful Apps From Simple Parts
+----------------------------------------
+
+Speaking of the :app:`Pyramid` structured "include" mechanism (see
+:meth:`~pyramid.config.Configurator.include`), it allows you to compose complex
+applications from multiple, simple Python packages. All the configuration
+statements that can be performed in your "main" Pyramid application can also be
+used in included packages. You can add views, routes, and subscribers, and even
+set authentication and authorization policies.
+
+If you need, you can extend or override the configuration of an existing
+application by including its configuration in your own and then modifying it.
+
+
+For example, if you want to reuse an existing application that already has a
+bunch of routes, you can just use the ``include`` statement with a
+``route_prefix``. All the routes of that application will be availabe, prefixed
+as you requested:
.. code-block:: python
:linenos:
@@ -120,91 +139,79 @@ For example:
See also :ref:`including_configuration` and
:ref:`building_an_extensible_app`.
-Flexible authentication and authorization
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Authenticate Users Your Way
+---------------------------
+
+:app:`Pyramid` ships with prebuilt well-tested authentication and authorization
+schemes out of the box. Using a scheme is a matter of configuration. So if you
+need to change approaches later, you need only update your configuration.
-Pyramid includes a flexible, pluggable authentication and authorization system.
-No matter where your user data is stored, or what scheme you'd like to use to
-permit your users to access your data, you can use a predefined Pyramid
-plugpoint to plug in your custom authentication and authorization code. If you
-want to change these schemes later, you can just change it in one place rather
-than everywhere in your code. It also ships with prebuilt well-tested
-authentication and authorization schemes out of the box. But what if you don't
-want to use Pyramid's built-in system? You don't have to. You can just write
-your own bespoke security code as you would in any other system.
+In addition, the system that handles authentication an authorization is
+flexible and pluggable. If you want to use another security add-on, or define
+your own, you can. And again, you need only update your application
+configuration to make the change.
Example: :ref:`enabling_authorization_policy`.
-Traversal
-~~~~~~~~~
-
-:term:`Traversal` is a concept stolen from :term:`Zope`. It allows you to
-create a tree of resources, each of which can be addressed by one or more URLs.
-Each of those resources can have one or more *views* associated with it. If
-your data isn't naturally treelike, or you're unwilling to create a treelike
-representation of your data, you aren't going to find traversal very useful.
-However, traversal is absolutely fantastic for sites that need to be
-arbitrarily extensible. It's a lot easier to add a node to a tree than it is to
-shoehorn a route into an ordered list of other routes, or to create another
-entire instance of an application to service a department and glue code to
-allow disparate apps to share data. It's a great fit for sites that naturally
-lend themselves to changing departmental hierarchies, such as content
-management systems and document management systems. Traversal also lends
-itself well to systems that require very granular security ("Bob can edit
-*this* document" as opposed to "Bob can edit documents").
+Build Trees of Resources
+------------------------
+
+:app:`Pyramid` supports :term:`Traversal`, a way of mapping URLs to a concrete
+tree of resources. If your application naturally consists of an arbitrary
+heirarchy of different types of content (like a CMS or a Document Management
+System), traversal is for you. If you have a requirement for a highly granular
+security model ("Jane can edit documents in *this* folder, but not *that*
+one"), traversal can be a powerful approach.
Examples: :ref:`hello_traversal_chapter` and
:ref:`much_ado_about_traversal_chapter`.
-Tweens
-~~~~~~
+Take Action on Each Request with Tweens
+---------------------------------------
+
+Pyramid has a system for applying arbitrary actions to each request or response
+called *tweens*. The system is similar in concept to WSGI :term:`middleware`,
+but can be more useful since they run in the Pyramid context, and have access
+to templates, request objects, and other niceties.
-Pyramid has a sort of internal WSGI-middleware-ish pipeline that can be hooked
-by arbitrary add-ons named "tweens". The debug toolbar is a "tween", and the
-``pyramid_tm`` transaction manager is also. Tweens are more useful than WSGI
-:term:`middleware` in some circumstances because they run in the context of
-Pyramid itself, meaning you have access to templates and other renderers, a
-"real" request object, and other niceties.
+The Pyramid debug toolbar is a "tween", as is the ``pyramid_tm`` transaction
+manager.
Example: :ref:`registering_tweens`.
-View response adapters
-~~~~~~~~~~~~~~~~~~~~~~
+Return What You Want From Your Views
+------------------------------------
-A lot is made of the aesthetics of what *kinds* of objects you're allowed to
-return from view callables in various frameworks. In a previous section in
-this document, we showed you that, if you use a :term:`renderer`, you can
-usually return a dictionary from a view callable instead of a full-on
-:term:`Response` object. But some frameworks allow you to return strings or
-tuples from view callables. When frameworks allow for this, code looks
-slightly prettier, because fewer imports need to be done, and there is less
-code. For example, compare this:
+We have shown before (in the :doc:`introduction`) how using a :term:`renderer`
+allows you to return simple Python dictionaries from your view code. But some
+frameworks allow you to return strings or tuples from view callables.
+When frameworks allow for this, code looks slightly prettier, because there are
+fewer imports, and less code. For example, compare this:
.. code-block:: python
:linenos:
+ from pyramid.response import Response
+
def aview(request):
- return "Hello world!"
+ return Response("Hello world!")
To this:
.. code-block:: python
:linenos:
- from pyramid.response import Response
-
def aview(request):
- return Response("Hello world!")
+ return "Hello world!"
-The former is "prettier", right?
+Nicer to look at, right?
-Out of the box, if you define the former view callable (the one that simply
-returns a string) in Pyramid, when it is executed, Pyramid will raise an
-exception. This is because "explicit is better than implicit", in most cases,
-and by default Pyramid wants you to return a :term:`Response` object from a
-view callable. This is because there's usually a heck of a lot more to a
-response object than just its body. But if you're the kind of person who
-values such aesthetics, we have an easy way to allow for this sort of thing:
+Out of the box, Pyramid will raise an exception if you try to run the second
+example above. After all, a view should return a response, and "explicit is
+better than implicit".
+
+But if you're a developer who likes the aesthetics of simplicity, Pyramid
+provides an way to support this sort of thing, the *response adapter*:
.. code-block:: python
:linenos:
@@ -217,12 +224,13 @@ values such aesthetics, we have an easy way to allow for this sort of thing:
response.content_type = 'text/html'
return response
+A new response adapter is registered in configuration:
+
if __name__ == '__main__':
config = Configurator()
config.add_response_adapter(string_response_adapter, basestring)
-Do that once in your Pyramid application at startup. Now you can return
-strings from any of your view callables, e.g.:
+With that, you may return strings from any of your view callables, e.g.:
.. code-block:: python
:linenos:
@@ -233,8 +241,8 @@ strings from any of your view callables, e.g.:
def goodbyeview(request):
return "Goodbye world!"
-Oh noes! What if you want to indicate a custom content type? And a custom
-status code? No fear:
+You can even use a response adapter to allow for custom content types and
+return codes:
.. code-block:: python
:linenos:
@@ -259,7 +267,7 @@ status code? No fear:
config.add_response_adapter(string_response_adapter, basestring)
config.add_response_adapter(tuple_response_adapter, tuple)
-Once this is done, both of these view callables will work:
+With this, both of these views will work as expected:
.. code-block:: python
:linenos:
@@ -270,20 +278,17 @@ Once this is done, both of these view callables will work:
def anotherview(request):
return (403, 'text/plain', "Forbidden")
-Pyramid defaults to explicit behavior, because it's the most generally useful,
-but provides hooks that allow you to adapt the framework to localized aesthetic
-desires.
-
.. seealso::
See also :ref:`using_iresponse`.
-"Global" response object
-~~~~~~~~~~~~~~~~~~~~~~~~
+Use Global Response Objects
+---------------------------
-"Constructing these response objects in my view callables is such a chore! And
-I'm way too lazy to register a response adapter, as per the prior section," you
-say. Fine. Be that way:
+Views have to return responses. But constructing them in view code is a chore.
+And perhaps registering a response adapter as shown above is just too much
+work. :app:`Pyramid` provides a global response object as well. You can just
+use it directly, if you prefer:
.. code-block:: python
:linenos:
@@ -298,17 +303,18 @@ say. Fine. Be that way:
See also :ref:`request_response_attr`.
-Automating repetitive configuration
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Extend Configuration
+--------------------
+
+Perhaps the :app:`Pyramid` configurator's syntax feels a bit verbose to you. Or
+possibly you would like to add a feature to configuration without asking the
+core developers to change Pyramid itself?
-Does Pyramid's configurator allow you to do something, but you're a little
-adventurous and just want it a little less verbose? Or you'd like to offer up
-some handy configuration feature to other Pyramid users without requiring that
-we change Pyramid? You can extend Pyramid's :term:`Configurator` with your own
-directives. For example, let's say you find yourself calling
-:meth:`pyramid.config.Configurator.add_view` repetitively. Usually you can
-take the boring away by using existing shortcuts, but let's say that this is a
-case where there is no such shortcut:
+You can extend Pyramid's :term:`Configurator` with your own directives. For
+example, let's say you find yourself calling
+:meth:`pyramid.config.Configurator.add_view` repetitively. Usually you can get
+rid of the boring with existing shortcuts, but let's say that this is a case
+where there is no such shortcut:
.. code-block:: python
:linenos:
@@ -352,26 +358,26 @@ the Configurator object:
config.add_route('xhr_route', '/xhr/{id}')
config.add_protected_xhr_views('my.package')
-Your previously repetitive configuration lines have now morphed into one line.
+Much better!
-You can share your configuration code with others this way, too, by packaging
-it up and calling :meth:`~pyramid.config.Configurator.add_directive` from
-within a function called when another user uses the
+You can share your configuration code with others, too. Package it up and call
+:meth:`~pyramid.config.Configurator.add_directive` from within a function
+called when another user uses the
:meth:`~pyramid.config.Configurator.include` method against your code.
.. seealso::
See also :ref:`add_directive`.
-Programmatic introspection
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+Introspect Your Application
+---------------------------
-If you're building a large system that other users may plug code into, it's
-useful to be able to get an enumeration of what code they plugged in *at
-application runtime*. For example, you might want to show them a set of tabs
-at the top of the screen based on an enumeration of views they registered.
+If you're building a large, pluggalbe system, it's useful to be able to get a
+list of what has been plugged in *at application runtime*. For example, you
+might want to show users a set of tabs at the top of the screen based on a list
+of the views they registered.
-This is possible using Pyramid's :term:`introspector`.
+:app:`Pyramid` provides an :term:`introspector` for just this purpose.
Here's an example of using Pyramid's introspector from within a view callable: