From 615813486fd3d14d0a748a76b2223297b5033f08 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Sun, 6 Mar 2011 14:31:18 -0500 Subject: [PATCH] More documentation updates and tweaking. --- docs/source/configuration.rst | 13 -------- docs/source/databases.rst | 52 ++++++++++++++++------------- docs/source/hooks.rst | 21 ++++++++---- docs/source/validation_n_errors.rst | 38 ++++++++++----------- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index abcee42..db4ac40 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -143,16 +143,3 @@ app is running I can access ``foo`` values like:: >>> conf.foo Config({'bar': True, 'baz': False}) - -Files and Structure -=================== -Pecan gives you a ``start.py`` file so you can manually run your application -from the command line. By default, this file requires a parameter which is -a configuration file without the ``.py`` extension. - -If you have a configuration file named ``config.py`` you would need to pass it -to ``start.py`` like:: - - python start.py config - - diff --git a/docs/source/databases.rst b/docs/source/databases.rst index 90f2522..5e42a44 100644 --- a/docs/source/databases.rst +++ b/docs/source/databases.rst @@ -1,25 +1,25 @@ .. _databases: -Working with Databases and ORM's +Working with Databases, Transactions, and ORM's ============= Out of the box, Pecan provides no opinionated support for working with databases, but it's easy to hook into your ORM of choice with minimal effort. This article -details best practices for integration the popular Python ORM, SQLAlchemy, into +details best practices for integrating the popular Python ORM, SQLAlchemy, into your Pecan project. -init_model and Preparing Your Model +``init_model`` and Preparing Your Model ---------------- Pecan's default quickstart project includes an empty stub directory for implementing your model as you see fit:: -. -└── test_project - ├── app.py - ├── __init__.py - ├── controllers - ├── model - │   ├── __init__.py - └── templates + . + └── test_project + ├── app.py + ├── __init__.py + ├── controllers + ├── model + │   ├── __init__.py + └── templates By default, this module contains a special method, ``init_model``:: @@ -90,10 +90,14 @@ Binding Within the Application ---------------- There are several approaches that can be taken to wrap your application's requests with calls to appropriate model function calls. One approach is WSGI middleware. We also recommend -Pecan hooks (see ref:`hooks`). Pecan comes with ``TransactionHook``, a hook which can -be used to wrap requests in transactions for you. To use it, you simply include it in your +Pecan :ref:`hooks`. Pecan comes with ``TransactionHook``, a hook which can +be used to wrap requests in transactions for you. To use it, simply include it in your project's ``app.py`` file and pass it a set of functions related to database binding:: + from pecan import conf, make_app + from pecan.hooks import TransactionHook + from test_project import model + app = make_app( conf.app.root, static_root = conf.app.static_root, @@ -111,28 +115,30 @@ project's ``app.py`` file and pass it a set of functions related to database bin ) For the above example, on HTTP POST, PUT, and DELETE requests, ``TransactionHook`` behaves in the -following manner:: +following manner: + +#. Before controller routing has been determined, ``model.start()`` is called. This function should bind to the appropriate SQLAlchemy engine and start a transaction. -#. Before controller routing has been determined, ``model.start()`` is called. This function -should bind to the appropriate SQLAlchemy engine and start a transaction. #. Controller code is run and returns. -#. If your controller or template rendering fails and raises an exception, ``model.rollback()`` -is called and the original exception is re-raised. This allows you to rollback your database -transaction to avoid committing work when exceptions occur in your application code. + +#. If your controller or template rendering fails and raises an exception, ``model.rollback()`` is called and the original exception is re-raised. This allows you to rollback your database transaction to avoid committing work when exceptions occur in your application code. + #. If the controller returns successfully, ``model.commit()`` and ``model.clear()`` are called. On idempotent operations (like HTTP GET and HEAD requests), TransactionHook behaves in the following -manner:: +manner: #. ``model.start_read_only()`` is called. This function should bind to your SQLAlchemy engine. + #. Controller code is run and returns. + #. If the controller returns successfully, ``model.clear()`` is called. Splitting Reads and Writes ---------------- -Employing the above strategy with ``TransactionHook`` makes it very simple to split database -reads and writes based upon HTTP methods (i.e., GET/HEAD requests are read and would potentially +Employing the strategy above with ``TransactionHook`` makes it very simple to split database +reads and writes based upon HTTP methods (i.e., GET/HEAD requests are read-only and would potentially be routed to a read-only database slave, while POST/PUT/DELETE requests require writing, and -would bind to a master database with read/write priveleges) It's also very easy extend +would always bind to a master database with read/write privileges). It's also very easy to extend ``TransactionHook`` or write your own hook implementation for more refined control over where and when database bindings are called. \ No newline at end of file diff --git a/docs/source/hooks.rst b/docs/source/hooks.rst index 8179d9c..4a20186 100644 --- a/docs/source/hooks.rst +++ b/docs/source/hooks.rst @@ -9,7 +9,7 @@ There is nothing wrong with WSGI Middleware, and actually, it is really easy to use middleware with Pecan, but it can be hard (sometimes impossible) to have access to Pecan's internals from within middleware. Hooks make this easier. -Hooks offer four ways of dealing with a request: +Hooks allow you to execute code at key points throughout the life cycle of your request: * ``on_route``: called before Pecan attempts to route a request to a controller @@ -22,21 +22,27 @@ Hooks offer four ways of dealing with a request: Implementation -------------- In the below example, we will write a simple hook that will gather -some information about the request and print it out to ``stdout``. +some information about the request and print it to ``stdout``. -Your hook implementation needs to import ``PecanHook`` so it can be used as a base class:: +Your hook implementation needs to import ``PecanHook`` so it can be used as a base class. +From there, you'll need to override the ``on_route``, ``before``, ``after``, or ``on_error`` methods:: from pecan.hooks import PecanHook class SimpleHook(PecanHook): + def before(self, state): + print "\nabout to enter the controller..." + def after(self, state): print "\nmethod: \t %s" % state.request.method print "\nresponse: \t %s" % state.response.status -``on_route``, ``before``, and ``after`` are passed a shared state object which includes useful +``on_route``, ``before``, and ``after`` are each passed a shared state object which includes useful information about the request, such as the request and response object, and which controller was chosen by Pecan's routing. + +``on_error`` is passed a shared state object **and** the original exception. Attaching Hooks -------------- @@ -63,16 +69,17 @@ using the ``__hooks__`` attribute on one or more controllers:: @expose('json') def index(self): + print "DO SOMETHING!" return dict() -Running it ----------- Now that our ``SimpleHook`` is included, let's see what happens when we run -the app and browse the application:: +the app and browse the application from our web browser:: pecan serve config.py serving on 0.0.0.0:8080 view at http://127.0.0.1:8080 + about to enter the controller... + DO SOMETHING! method: GET response: 200 OK diff --git a/docs/source/validation_n_errors.rst b/docs/source/validation_n_errors.rst index 50c5faa..6df6b01 100644 --- a/docs/source/validation_n_errors.rst +++ b/docs/source/validation_n_errors.rst @@ -9,7 +9,7 @@ error handling activities, like: * Transforming strings from form submissions into useful Python objects. * Simplifying the process of re-displaying form values and associated error messages inline. -Rather than re-inventing the wheel, Pecan uses FormEncode for schemas and form validation. +Rather than re-inventing the wheel, Pecan uses `FormEncode `_ for schemas and form validation. Writing and Applying Schemas ------------------------------ @@ -37,7 +37,7 @@ Pecan's ``expose`` decorator:: Validating JSON Content ------------------------------ In addition to simple form arguments, Pecan also makes it easy to validate JSON request bodies. -Often, especially in AJAX requests, the request content is encoded as a JSON in the request body. +Often, especially in AJAX requests, the request content is encoded as JSON in the request body. Pecan's validation can handle the decoding for you and apply schema validation to the decoded data structure:: @@ -94,7 +94,6 @@ This is especially useful when used in combination with generic controller methods:: from pecan import request, expose - from formencode import Schema, validators as v class ProfileSchema(Schema): @@ -121,7 +120,8 @@ controller methods:: """ This method will do something with POST arguments. If the schema validation fails, an internal redirect will - cause the `profile.html` template to be rendered. + cause the `profile.html` template to be rendered via the + ``index_get`` method. """ name = kw.get('name') @@ -133,27 +133,27 @@ In this example, when form validation errors occur (for example, the email provi Pecan will handle pre-filling the form values in ``profile.html`` for you. Additionally, inline errors will be appended to the template using FormEncode's ``htmlfill``. -Bypassing htmlfill +Bypassing ``htmlfill`` ------------------------------ -Sometimes you want certain fields in your templates to be ignored (i.e., not pre-filled) by htmlfill. +Sometimes you want certain fields in your templates to be ignored (i.e., not pre-filled) by ``htmlfill``. A perfect use case for this is password and hidden input fields. The default Pecan template namespace includes a built-in function, ``static``, which allows you to enforce a static value for form fields, -preventing htmlfill from filling it in form arguments:: +preventing ``htmlfill`` from filling in submitted form variables:: -
-
-
Username:
-
-
Password:
-
- -
- -
+
+
+
Username:
+
+
Password:
+
+ +
+ +
-Working with variabledecode +Working with ``variabledecode`` ------------------------------ -Pecan also lets you take advantage of FormEncode's variabledecode for transforming flat HTML form +Pecan also lets you take advantage of FormEncode's ``variabledecode`` for transforming flat HTML form submissions into nested structures:: from pecan import expose