1 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
3 <title>Twisted Documentation: Storing Objects in the Session</title>
4 <link href="../stylesheet.css" rel="stylesheet" type="text/css"/>
8 <h1 class="title">Storing Objects in the Session</h1>
9 <div class="toc"><ol/></div>
13 <p>This example shows you how you can persist objects across requests in the
16 <p>As was discussed <a href="session-basics.html" shape="rect">previously</a>, instances
17 of <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.web.server.Session.html" title="twisted.web.server.Session">Session</a></code> last as long as
18 the notional session itself does. Each time <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.web.server.Request.getSession.html" title="twisted.web.server.Request.getSession">Request.getSession</a></code> is called, if the session
19 for the request is still active, then the same <code>Session</code> instance is
20 returned as was returned previously. Because of this, <code>Session</code>
21 instances can be used to keep other objects around for as long as the session
24 <p>It's easier to demonstrate how this works than explain it, so here's an
27 <pre class="shell" xml:space="preserve">
28 >>> from zope.interface import Interface, Attribute, implements
29 >>> from twisted.python.components import registerAdapter
30 >>> from twisted.web.server import Session
31 >>> class ICounter(Interface):
32 ... value = Attribute("An int value which counts up once per page view.")
34 >>> class Counter(object):
35 ... implements(ICounter)
36 ... def __init__(self, session):
39 >>> registerAdapter(Counter, Session, ICounter)
40 >>> ses = Session(None, None)
41 >>> data = ICounter(ses)
42 >>> print data
43 <__main__.Counter object at 0x8d535ec>
44 >>> print data is ICounter(ses)
49 <p><i>What?</i>, I hear you say.</p>
51 <p>What's shown in this example is the interface and adaption-based
52 API which <code>Session</code> exposes for persisting state. There are
53 several critical pieces interacting here:</p>
56 <li><code>ICounter</code> is an interface which serves several purposes. Like
57 all interfaces, it documents the API of some class of objects (in this case,
58 just the <code>value</code> attribute). It also serves as a key into what is
59 basically a dictionary within the session object: the interface is used to
60 store or retrieve a value on the session (the <code>Counter</code> instance,
62 <li><code>Counter</code> is the class which actually holds the session data in
63 this example. It implements <code>ICounter</code> (again, mostly for
64 documentation purposes). It also has a <code>value</code> attribute, as the
65 interface declared.</li>
66 <li>The <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.python.components.registerAdapter.html" title="twisted.python.components.registerAdapter">registerAdapter</a></code> call sets up the
67 relationship between its three arguments so that adaption will do what we
68 want in this case.</li>
69 <li>Adaption is performed by the expression <code>ICounter(ses)</code>. This
70 is read as : adapt <code>ses</code> to <code>ICounter</code>. Because
71 of the <code>registerAdapter</code> call, it is roughly equivalent
72 to <code>Counter(ses)</code>. However (because of certain
73 things <code>Session</code> does), it also saves the <code>Counter</code>
74 instance created so that it will be returned the next time this adaption is
75 done. This is why the last statement produces <code>True</code>.</li>
78 <p>If you're still not clear on some of the details there, don't worry about it
79 and just remember this: <code>ICounter(ses)</code> gives you an object you can
80 persist state on. It can be as much or as little state as you want, and you can
81 use as few or as many different <code>Interface</code> classes as you want on a
82 single <code>Session</code> instance.</p>
84 <p>With those conceptual dependencies out of the way, it's a very short step to
85 actually getting persistent state into a Twisted Web application. Here's an
86 example which implements a simple counter, re-using the definitions from the
89 <pre class="python"><p class="py-linenumber">1
97 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">resource</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Resource</span>
99 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CounterResource</span>(<span class="py-src-parameter">Resource</span>):
100 <span class="py-src-keyword">def</span> <span class="py-src-identifier">render_GET</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">request</span>):
101 <span class="py-src-variable">session</span> = <span class="py-src-variable">request</span>.<span class="py-src-variable">getSession</span>()
102 <span class="py-src-variable">counter</span> = <span class="py-src-variable">ICounter</span>(<span class="py-src-variable">session</span>)
103 <span class="py-src-variable">counter</span>.<span class="py-src-variable">value</span> += <span class="py-src-number">1</span>
104 <span class="py-src-keyword">return</span> <span class="py-src-string">"Visit #%d for you!"</span> % (<span class="py-src-variable">counter</span>.<span class="py-src-variable">value</span>,)
107 <p>Pretty simple from this side, eh? All this does is
108 use <code>Request.getSession</code> and the adaption from above, plus some
109 integer math to give you a session-based visit counter.</p>
111 <p>Here's the complete source for an <a href="rpy-scripts.html" shape="rect">rpy script</a>
112 based on this example:</p>
114 <pre class="python"><p class="py-linenumber"> 1
139 </p><span class="py-src-variable">cache</span>()
141 <span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Interface</span>, <span class="py-src-variable">Attribute</span>, <span class="py-src-variable">implements</span>
142 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span>.<span class="py-src-variable">components</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">registerAdapter</span>
143 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">server</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Session</span>
144 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span>.<span class="py-src-variable">resource</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Resource</span>
146 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ICounter</span>(<span class="py-src-parameter">Interface</span>):
147 <span class="py-src-variable">value</span> = <span class="py-src-variable">Attribute</span>(<span class="py-src-string">"An int value which counts up once per page view."</span>)
149 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Counter</span>(<span class="py-src-parameter">object</span>):
150 <span class="py-src-variable">implements</span>(<span class="py-src-variable">ICounter</span>)
151 <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">session</span>):
152 <span class="py-src-variable">self</span>.<span class="py-src-variable">value</span> = <span class="py-src-number">0</span>
154 <span class="py-src-variable">registerAdapter</span>(<span class="py-src-variable">Counter</span>, <span class="py-src-variable">Session</span>, <span class="py-src-variable">ICounter</span>)
156 <span class="py-src-keyword">class</span> <span class="py-src-identifier">CounterResource</span>(<span class="py-src-parameter">Resource</span>):
157 <span class="py-src-keyword">def</span> <span class="py-src-identifier">render_GET</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">request</span>):
158 <span class="py-src-variable">session</span> = <span class="py-src-variable">request</span>.<span class="py-src-variable">getSession</span>()
159 <span class="py-src-variable">counter</span> = <span class="py-src-variable">ICounter</span>(<span class="py-src-variable">session</span>)
160 <span class="py-src-variable">counter</span>.<span class="py-src-variable">value</span> += <span class="py-src-number">1</span>
161 <span class="py-src-keyword">return</span> <span class="py-src-string">"Visit #%d for you!"</span> % (<span class="py-src-variable">counter</span>.<span class="py-src-variable">value</span>,)
163 <span class="py-src-variable">resource</span> = <span class="py-src-variable">CounterResource</span>()
166 <p>One more thing to note is the <code>cache()</code> call at the top
167 of this example. As with the <a href="http-auth.html" shape="rect">previous
168 example</a> where this came up, this rpy script is stateful. This
169 time, it's the <code>ICounter</code> definition and
170 the <code>registerAdapter</code> call that need to be executed only
171 once. If we didn't use <code>cache</code>, every request would define
172 a new, different interface named <code>ICounter</code>. Each of these
173 would be a different key in the session, so the counter would never
178 <p><a href="../index.html">Index</a></p>
179 <span class="version">Version: 12.1.0</span>