|
From: <sub...@co...> - 2005-11-20 20:18:53
|
Author: ianb
Date: 2005-11-20 20:18:46 +0000 (Sun, 20 Nov 2005)
New Revision: 1305
Modified:
FormEncode/trunk/docs/Design.txt
Log:
Updated design doc
Modified: FormEncode/trunk/docs/Design.txt
===================================================================
--- FormEncode/trunk/docs/Design.txt 2005-11-20 19:47:49 UTC (rev 1304)
+++ FormEncode/trunk/docs/Design.txt 2005-11-20 20:18:46 UTC (rev 1305)
@@ -17,38 +17,50 @@
FormEncode performs look-before-you-leap validation. The idea being
that you check all the data related to an operation, then apply it.
-The alternative might be a transactional system, where you just start
+This is in contrast to a transactional system, where you just start
applying the data and if there's a problem you raise an exception.
Someplace else you catch the exception and roll back the transaction.
-Of course FormEncode works fine with such a system, but unlike this
-you can use it without transactions.
+Of course FormEncode works fine with such a system, but because
+nothing is done until everything validators, you can use this without
+transactions.
FormEncode generally works on primitive types (though you could extend
-it to deal with your own types if you wish). These are things like
-strings, lists, dictionaries, integers, etc. This fits in with
-look-before-you-leap; often your domain objects won't exist until
-after you apply the user's request, so it's necessary to work on an
-early form of the data. Also, FormEncode doesn't know anything about
-your domain objects or classes; it's just easier to keep it this way.
+it to deal with your own types if you wish; `formencode.sqlschema
+<module-formencode.sqlschema.html>`_ is an example of this). These
+are things like strings, lists, dictionaries, integers, etc. This
+fits in with look-before-you-leap; often your domain objects won't
+exist until after you apply the user's request, so it's necessary to
+work on an early form of the data. Also, FormEncode doesn't know
+anything about your domain objects or classes; it's just easier to
+keep it this way.
Validation only operates on a single "value" at a time. This is
Python, collections are easy, and collections are themselves a single
"value" made up of many pieces. A "Schema validator" is a validator
-made up of many subvalidators.
+made up of many subvalidators. By using this single metaphor, without
+separating the concept of "field" and "form", it is possible to create
+reusable validators that work on compound structures, to validate
+"whole forms" instead of just single fields, and to support better
+validation composition.
+Also, "validation" and "conversion" are generally applied at the same
+time. In the documentation this is frequently just referred to as
+"validation", but anywhere validation can happen, conversion can also
+happen.
+
Domain Objects
==============
These are your objects, specific to your application. I know nothing
about them, and cannot know. FormEncode doesn't do anything with
-these objects, and doesn't try to know anything about them. At all.
+these objects, and doesn't try to know anything about them.
Validation as directional, not intrinsic
========================================
-One false start I've made is an attempt to tie validators into the
-objects they validate against. E.g., you might have a SQLObject_
-class like::
+One false start from earlier projects was an attempt to tie validators
+into the objects they validate against. E.g., you might have a
+SQLObject_ class like::
class Address(SQLObject):
fname = StringCol(notNull=True)
@@ -59,8 +71,10 @@
It is tempting to take the restrictions of the ``Address`` class and
automatically come up with a validation schema. This may yet be a
-viable goal, but in practical terms validation tends to be both more
-and less restrictive.
+viable goal (and to a degree is attainable), but in practical terms
+validation tends to be both more and less restrictive. Also,
+validation is contextual; what validation you apply is dependent on
+the source of the data.
Often in an API we are more restrictive than we may be in a user
interface, demanding that everything be specified explicitly. In a UI
@@ -74,15 +88,14 @@
restriction on the UI keeping in mind that it can more easily be
relaxed and refined than a restriction in the domain objects or
underlying database. Also, we may trust the programmer to use the API
-in a reasonable way, but we seldom trust the user under any
-circumstance.
+in a reasonable way, but we seldom trust user data in the same way.
-In essence, there is an "inside" and an "outside". FormEncode is a
-toolkit for bridging those two areas in a sensible and secure way.
-The specific way we bridge this depends on the nature of the user
-interface. An XML-RPC interface can make some assumptions that a GUI
-cannot make. An HTML interface can typically make even less
-assumptions, including the basic integrity of the input data. It
+In essence, there is an "inside" and an "outside" to the program.
+FormEncode is a toolkit for bridging those two areas in a sensible and
+secure way. The specific way we bridge this depends on the nature of
+the user interface. An XML-RPC interface can make some assumptions
+that a GUI cannot make. An HTML interface can typically make even
+less assumptions, including the basic integrity of the input data. It
isn't reasonable that the object should know about all means of
inputs, and the varying UI requirements of those inputs; user
interfaces are volatile, and more art than science, but domain objects
@@ -91,11 +104,9 @@
It also didn't work well to annotate domain objects with validation
schemas, though the option remains open. This is experimentation that
-belongs outside of FormEncode, simply because it's more specific to
-your domain than it is to FormEncode.
+belongs outside of the core of FormEncode, simply because it's more
+specific to your domain than it is to FormEncode.
-.. _adapted:
-
Two sides, two aspects
======================
@@ -112,17 +123,17 @@
Similarly, there's two sides to the system, the foreign data and the
local data. In Validator the local data is called "python" (meaning,
a natural Python data structure), so we convert ``to_python`` and
-``from_python``. Unlike some systems, Validator explicitly converts
-*both* directions.
+``from_python``. Unlike some systems, validators explicitly convert
+in *both* directions.
For instance, consider the date conversion. In one form, you may want
a date like ``mm/dd/yyyy``. It's easy enough to make the necessary
converter; but the date object that the converter produces doesn't
-know how it's supposed to be formatted for that form. ``repr`` or
-*any* method that binds an object to its form representation is a bad
-idea. The converter best knows how to undo its work. So a date
-converter that expects ``mm/dd/yyyy`` will also know how to turn a
-datetime into that format.
+know how it's supposed to be formatted for that form. Using
+``repr()`` or *any* method that binds an object to its form
+representation is a bad idea. The converter best knows how to undo
+its work. So a date converter that expects ``mm/dd/yyyy`` will also
+know how to turn a datetime into that format.
(This becomes even more interesting with compound validators.)
@@ -140,13 +151,28 @@
something that is tied to the framework, the presentation layer, and
the domain objects, and FormEncode doesn't know anything about those.
-Instead FormEncode uses htmlfill_. *You* produce the form however you
-want. Write it out by hand. Use a templating language. Whatever.
-Then htmlfill (which specifically understands HTML) fills in the form
-and any error messages.
+FormEncode does provide `htmlfill <htmlfill.html>`_. *You* produce
+the form however you want. Write it out by hand. Use a templating
+language. Use a form generator. Whatever. Then htmlfill (which
+specifically understands HTML) fills in the form and any error
+messages. There are several advantages to this:
-.. _htmlfill: htmlfill.html
+* Using ``htmlfill``, form generation is easy. You can just think
+ about how to map a form description or model class to simple HTML.
+ You don't have to think about any of the low-level stuff about
+ filling attributes with defaults or past request values.
+* ``htmlfill`` works with anything that produces HTML. There's zero
+ preference for any particular templating language, or even general
+ style of templating language.
+
+* If you do form generation, but it later turns out to be
+ insufficiently flexible, you can put the generated form into your
+ template and extend it there; you'll lose automatic synchronization
+ with your models, but you won't lose any functionality.
+
+* Hand-written forms are just as functional as generated forms.
+
Declarative and Imperative
==========================
@@ -155,9 +181,7 @@
styles often look better (specifically using subclassing instead of
construction). You are free to build the objects either way.
-For instance, one extension to ``htmlfill``
-(``htmlfill_schemabuilder``) looks for special attributes in an HTML
-form and builds a validator from that. Even though validation is
-stored in a separate object from your domain, you can build those
-validators programmatically.
+An example of programmatically building form generation:
+``htmlfill_schemabuilder`` looks for special attributes in an HTML
+form and builds a validation schema from that.
|