SVN: r25810 - trunk/quixote/doc
David Binger <dbinger-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Tue, 21 Dec 2004 11:40:17 -0500
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: dbinger
Date: 2004-12-21 08:36:21 -0500 (Tue, 21 Dec 2004)
New Revision: 25810
Modified:
trunk/quixote/doc/session-mgmt.txt
Log:
Update session-mgmt.txt to agree with Quixote 2.
Modified: trunk/quixote/doc/session-mgmt.txt
===================================================================
--- trunk/quixote/doc/session-mgmt.txt 2004-12-21 13:22:37 UTC (rev 25809)
+++ trunk/quixote/doc/session-mgmt.txt 2004-12-21 13:36:21 UTC (rev 25810)
@@ -1,7 +1,3 @@
-***
-This has not been updated for Quixote 2.
-***
-
Quixote Session Management
==========================
@@ -25,12 +21,8 @@
For further reading: the standard for cookies that is approximately
implemented by most current browsers is RFC 2109; the latest version of
-the standard is RFC 2965. Those RFCs can be found here:
+the standard is RFC 2965.
- ftp://ftp.isi.edu/in-notes/rfc2109.txt
-
- ftp://ftp.isi.edu/in-notes/rfc2965.txt
-
In a nutshell, session management with Quixote works like this:
* when a user-agent first requests a page from a Quixote application
@@ -38,7 +30,8 @@
and generates a session ID (a random 64-bit number). The Session
object is attached to the current HTTPRequest object, so that
application code involved in processing this request has access to
- the Session object.
+ the Session object. The get_session() function provides uniform
+ access to the current Session object.
* if, at the end of processing that request, the application code has
stored any information in the Session object, Quixote saves the
@@ -64,9 +57,9 @@
looks up the corresponding Session object in its SessionManager. If
there is no such session, the session cookie is bogus or
out-of-date, so Quixote raises SessionError; ultimately the user
- gets an error page. Otherwise, the Session object is attached to
- the HTTPRequest object that is available to all application code
- used to process the request.
+ gets an error page. Otherwise, the Session object is made
+ available, through the get_session() function, as the application
+ code processes the request.
There are two caveats to keep in mind before proceeding, one major and
one minor:
@@ -86,21 +79,11 @@
Session management demo
-----------------------
-There's a simple demo of Quixote's session management in
-``demo/session_demo.cgi`` and ``demo/session.ptl``. The demo implements
-a simple session persistence scheme (each session is written to a
-separate pickle file in ``/tmp/quixote-session-demo``), so running it
-through CGI is just fine.
+There's a simple demo of Quixote's session management in demo/altdemo.py.
+If the durus (http://www.mems-exchange.org/software/durus/) package is
+installed, the demo uses a durus database to store sessions, so sessions
+will be preserved, even if your are running it with plain cgi.
-I'll assume that you've added a rewrite rule so that requests for
-``/qsdemo/`` are handled by ``session_demo.cgi``, similar to the
-rewriting for ``/qdemo/`` described in web-server.txt. Once that's
-done, point your browser at ::
-
- http://<hostname>/qsdemo/
-
-and play around.
-
This particular application uses sessions to keep track of just two
things: the user's identity and the number of requests made in this
session. The first is addressed by Quixote's standard Session class --
@@ -109,57 +92,50 @@
user's name, which is entered by the user.
Tracking the number of requests is a bit more interesting: from the
-DemoSession class in session_demo.cgi::
+DemoSession class in altdemo.py::
- def __init__ (self, request, id):
- Session.__init__(self, request, id)
+ def __init__ (self, id):
+ Session.__init__(self, id)
self.num_requests = 0
- def start_request (self, request):
- Session.start_request(self, request)
+ def start_request (self):
+ Session.start_request(self)
self.num_requests += 1
-When the session is created, we initialize the request counter; and when
-we start processing each request, we increment it.
+When the session is created, we initialize the request counter; and
+when we start processing each request, we increment it. Using the
+session information in the application code is simple. If you want the
+value of the user attribute of the current session, just call
+get_user(). If you want some other attribute or method Use
+get_session() to get the current Session if you need access to other
+attributes (such as ``num_requests`` in the demo) or methods of the
+current Session instance.
-Using the session information in the application code is simple. For
-example, here's the PTL code that checks if the user has logged in
-(identified herself) yet, and generates a login form if not::
+Note that the Session class initializes the user attribute to None,
+so get_user() will return None if no user has been identified for
+this session. Application code can use this to change behavior,
+as in the following::
- session = request.session
- if session.user is None:
- '''
- <p>You haven\'t introduced yourself yet.<br>
- Please tell me your name:
- '''
- login_form()
+ if not get_user():
+ content += htmltext('<p>%s</p>' % href('login', 'login'))
+ else:
+ content += htmltext(
+ '<p>Hello, %s.</p>') % get_user()
+ content += htmltext('<p>%s</p>' % href('logout', 'logout'))
-(The ``login_form()`` template just emits a simple HTML form -- see
-``demo/session.ptl`` for full source.)
-If the user has already identified herself, then she doesn't need to do
-so again -- so the other branch of that ``if`` statement simply prints a
-friendly greeting::
-
- else:
- ('<p>Hello, %s. Good to see you again.</p>\n'
- % html_quote(session.user))
-
Note that we must quote the user's name, because they are free to enter
anything they please, including special HTML characters like ``&`` or
``<``.
Of course, ``session.user`` will never be set if we don't set it
ourselves. The code that processes the login form is just this (from
-``login()`` in ``demo/session.ptl``)::
+``login()`` in ``demo/altdemo.py``) ::
- if request.form:
- user = request.form.get("name")
- if not user:
- raise QueryError("no user name supplied")
+ if get_field("name"):
+ session = get_session()
+ session.set_user(get_field("name")) # This is the important part.
- session.user = user
-
This is obviously a very simple application -- we're not doing any
verification of the user's input. We have no user database, no
passwords, and no limitations on what constitutes a "user name". A real
@@ -251,7 +227,7 @@
The first one is fairly obvious and just good practice. The second is
essential, and not at all obvious. The has_info() method exists because
SessionManager does not automatically hang on to all session objects;
-this is a defence against clients that ignore cookies, making your
+this is a defense against clients that ignore cookies, making your
session manager create lots of session objects that are just used once.
As long as those session objects are not saved, the burden imposed by
these clients is not too bad -- at least they aren't sucking up your
@@ -298,59 +274,50 @@
number of hooks, most in the SessionManager class, that let you plug in
your preferred persistence mechanism.
-The first and most important hook is in the SessionManager constructor:
-you can provide an alternate mapping object that SessionManager will use
-to store session objects in. By default, SessionManager uses an
-ordinary dictionary; if you provide a mapping object that implements
-persistence, then your session data will automatically persist across
-processes. For example, you might use the standard 'shelve' module,
-which provides a mapping object on top of a DBM or Berkeley DB file::
+The first and most important hook is in the SessionManager
+constructor: you can provide an alternate mapping object that
+SessionManager will use to store session objects in. By default,
+SessionManager uses an ordinary dictionary; if you provide a mapping
+object that implements persistence, then your session data will
+automatically persist across processes.
+The second hook (two hooks, really) apply if you use a transactional
+persistence mechanism to provide your SessionManager's mapping. The
+``altdemo.py`` script does this with Durus, if the durus package is
+installed, but you could also use ZODB or a relational database for
+this purpose. The hooks make sure that session (and other) changes
+get committed or aborted at the appropriate times. SessionManager
+provides two methods for you to override: ``forget_changes()`` and
+``commit_changes()``. ``forget_changes()`` is called by
+SessionPublisher whenever a request crashes, ie. whenever your
+application raises an exception other than PublishError.
+``commit_changes()`` is called for requests that complete
+successfully, or that raise a PublishError exception. You'll have to
+use your own SessionManager subclass if you need to take advantage of
+these hooks for transactional session persistence.
+
+The third available hook is the Session's is_dirty() method. This is
+used when your mapping class uses a more primitive storage mechanism,
+as, for example, the standard 'shelve' module, which provides a
+mapping object on top of a DBM or Berkeley DB file::
+
import shelve
sessions = shelve.open("/tmp/quixote-sessions")
session_mgr = SessionManager(session_mapping=sessions)
-For a persistent mapping implementation that doesn't require any
-external libraries, see the DirMapping class in
-``demo/session_demo.cgi``.
-
If you use one of these relatively simple persistent mapping types,
you'll also need to override ``is_dirty()`` in your Session class.
That's in addition to overriding ``has_info()``, which determines if a
session object is *ever* saved; ``is_dirty()`` is only called on
-sessions that have already been added to the session mapping, to see if
-they need to be "re-added". The default implementation always returns
-false, because once an object has been added to a normal dictionary,
-there's no need to add it again. However, with simple persistent
-mapping types like shelve and DirMapping, you need to store the object
-again each time it changes. Thus, ``is_dirty()`` should return true if
-the session object needs to be re-written. For a simple, naive, but
-inefficient implementation, making is_dirty an alias for ``has_info()``
-will work -- that just means that once the session has been written
-once, it will be re-written on every request. (This is what DemoSession
-in ``demo/session_demo.cgi`` does.)
+sessions that have already been added to the session mapping, to see
+if they need to be "re-added". The default implementation always
+returns false, because once an object has been added to a normal
+dictionary, there's no need to add it again. However, with simple
+persistent mapping types like shelve, you need to store the object
+again each time it changes. Thus, ``is_dirty()`` should return true
+if the session object needs to be re-written. For a simple, naive,
+but inefficient implementation, making is_dirty an alias for
+``has_info()`` will work -- that just means that once the session has
+been written once, it will be re-written on every request.
-The third and final part of the persistence interface only applies if
-you are using a transactional persistence mechanism, such as ZODB or an
-industrial-strength relational database. In that case, you need a place
-to commit or abort the transaction that contains pending changes to the
-current session. SessionManager provides two methods for you to
-override: ``abort_changes()`` and ``commit_changes()``.
-``abort_changes()`` is called by SessionPublisher whenever a request
-crashes, ie. whenever your application raises an exception other than
-PublishError. ``commit_changes()`` is called for requests that complete
-successfully, or that raise a PublishError exception. They are defined
-as follows::
- def abort_changes (self, session):
- """abort_changes(session : Session)"""
-
- def commit_changes (self, session):
- """commit_changes(session : Session)"""
-
-Obviously, you'll have to write your own SessionManager subclass if you
-need to take advantage of these hooks for transactional session
-persistence.
-
-
-$Id$