modelindepth.html   [plain text]


<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Model In Depth</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Model In Depth</h1><div class="toc"><ol><li><a href="#auto0">Main Concepts</a></li><li><a href="#auto1">Submodel Paths</a></li><li><a href="#auto2">The Model Stack and Relative Submodel Paths</a></li><li><a href="#auto3">IModel Adapters</a></li><li><a href="#auto4">Registering an IModel adapter for a class</a></li><li><a href="#auto5">Model Factories</a></li></ol></div><div class="content"><span></span><div class="note"><strong>Note: </strong><p>
This HOWTO documents the Model class, part of the Woven framework. The Woven framework should not be used for new projects. The newer <a href="http://www.divmod.org/Home/Projects/Nevow/">Nevow</a> framework, available as part of the <a href="http://www.divmod.org/Quotient/">Quotient</a> project, is a simpler framework with consistent semantics and better testing and is strongly recommended over Woven.
</p><p>
The Woven documentation below is maintained only for users with an existing Woven codebase.
</p></div><img src="../img/model.png" title="Model in Relation to Controller and View" /><p>Model objects provide data to View objects as a Page is being rendered.</p><h2>Main Concepts<a name="auto0"></a></h2><ul><li>Root Models are the data entry point for every woven Page. All Model data
for display on a Page should be made available through this Root Model.
Described further in the <a href="pageindepth.html#rootmodels">Page section</a>.</li><li><a href="#submodelpaths">Submodel Paths</a> allow Woven to locate the correct Model data for a node.</li><li><a href="#modelstack">The Model Stack</a> is how Woven keeps track of which Model object is currently <q>in scope</q>. Instead of specifying <code>model=</code> attributes in the Template with <q>absolute submodel paths</q>, you can specify a model relative to the top of the <q>Model stack</q> with a <q>relative submodel path</q>.</li><li><a href="#imodel">IModel Adapters</a> allow you to write wrappers for existing objects. Subclassing a base Model in the <code>models</code> module will make writing an IModel Adapter easier.</li><li><a href="#modelfactories">Model Factories</a> allow you to produce Model objects on demand with a Python method.</li></ul><h2>Submodel Paths<a name="auto1"></a></h2><a name="submodelpaths"></a><p>Each Model Woven has access to in the tree has a <q>submodel path</q>. Submodel paths start at the Root Model and specify each segment Woven must follow to locate the Model. Submodel paths are slash-separated strings similar to filesystem paths. For the basic Python container types, Dictionaries and Lists, a submodel path segment is simply the key into the container. Given the model:</p><pre class="python">
<span class="py-src-variable">model</span> <span class="py-src-op">=</span> <span class="py-src-op">{</span><span class="py-src-string">'name'</span><span class="py-src-op">:</span> <span class="py-src-string">&quot;Donovan&quot;</span><span class="py-src-op">,</span><span class="py-src-nl">
</span>         <span class="py-src-string">'interests'</span><span class="py-src-op">:</span> <span class="py-src-op">[</span><span class="py-src-string">&quot;Computers&quot;</span><span class="py-src-op">,</span> <span class="py-src-string">&quot;Music&quot;</span><span class="py-src-op">]</span><span class="py-src-op">,</span><span class="py-src-nl">
</span>         <span class="py-src-string">'inventory'</span><span class="py-src-op">:</span> <span class="py-src-op">{</span><span class="py-src-string">'dresser'</span><span class="py-src-op">:</span> <span class="py-src-op">[</span><span class="py-src-string">'socks'</span><span class="py-src-op">,</span><span class="py-src-string">'shirts'</span><span class="py-src-op">]</span><span class="py-src-op">,</span><span class="py-src-nl">
</span>                       <span class="py-src-string">'studio'</span><span class="py-src-op">:</span> <span class="py-src-op">[</span><span class="py-src-string">'Audiowerk8'</span><span class="py-src-op">,</span> <span class="py-src-string">'800mhz PC'</span><span class="py-src-op">,</span> <span class="py-src-string">'iMac'</span><span class="py-src-op">]</span><span class="py-src-op">,</span><span class="py-src-nl">
</span>                      <span class="py-src-op">}</span><span class="py-src-nl">
</span>         <span class="py-src-op">}</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></pre><img src="../img/plone_root_model.png" title="Model Tree Diagram" /><p>The following submodel paths are valid:</p><ul><li><code>/</code> specifies the Root Model, a Dictionary</li><li><code>/name</code> specifies the <code>name</code> entry in the Dictionary, a String</li><li><code>/interests</code> specifies the <code>interests</code> entry in the Dictionary, a List</li><li><code>/interests/0</code> specifies the first element of the <code>interests</code> list, a String</li><li><code>/interests/1</code> specifies the second element of the <code>interests</code> list, a String</li><li><code>/inventory/dresser/0</code> specifies the first element of the <code>dresser</code> list in the <code>inventory</code> dictionary, a String</li></ul><p>etc...</p><p>When woven encouters a <code>model=</code> directive on a node, it will look up the model and pass it to the View object that will render the node:</p><pre>
&lt;html&gt;
    &lt;body&gt;
        &lt;h3 model=&quot;/interests/3&quot; view=&quot;Text&quot; /&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre><h2>The Model Stack and Relative Submodel Paths<a name="auto2"></a></h2><a name="modelstack"></a><p>While <q>absolute model paths</q> are useful for specifying exactly which Model data you want associated with a node, the more frequent use case is to specify a <q>relative model path</q> which is a path relative to the Model currently on top of the <q>Model stack</q>. Relative model paths are easy to distinguish because they do not begin with a slash.</p><p>When Woven encounters a node with a <code>model=</code> attribute, it looks up the Model object and places it on top of the <q>Model stack</q>. During the processing of this node and all of the node's child nodes, this Model object remains on the top of the stack. Once all child nodes have completed processing, it is popped off of the Model stack.</p><p>This means that child nodes can refer to elements of the Model on top of the Model stack with relative submodel paths. For example, we may wish to render the <q>interests</q> list from the above example as two separate HTML elements. To do so, we first place the <q>interests</q> list on top of the Model stack, and then refer to elements of this list:</p><pre>
&lt;html&gt;
  I am interested in:
  &lt;div model=&quot;interests&quot; view=&quot;None&quot;&gt;
    &lt;p&gt;First thing: &lt;span model=&quot;0&quot; view=&quot;Text&quot; /&gt;&lt;/p&gt;
    &lt;p&gt;Second thing: &lt;span model=&quot;1&quot; view=&quot;Text&quot; /&gt;&lt;/p&gt;
  &lt;/div&gt;
&lt;/html&gt;
</pre><p>In this case, the <q>interests</q> list was in scope for the duration of the &lt;div&gt; tag, and the individual interest strings were in scope for the duration of the individual &lt;span&gt; tags.</p><p>The List widget uses this Stack concept to operate on DOM nodes abstractly, without knowing or caring what directives will occur when the child nodes it returns are handled. We can also use the familiar <code>.</code> and <code>..</code> concepts from unix shell syntax to refer to Models:</p><pre>
&lt;html&gt;
  &lt;div model=&quot;interests&quot; view=&quot;List&quot;&gt;
    &lt;h3 pattern=&quot;listHeader&quot; model=&quot;../name&quot; view=&quot;Text&quot; /&gt;&gt;
    &lt;p pattern=&quot;listItem&quot; view=&quot;Text&quot; /&gt;
    &lt;h6 pattern=&quot;listFooter&quot; model=&quot;.&quot; view=&quot;Text&quot; /&gt;
  &lt;/div&gt;
&lt;html&gt;
</pre><p>The List widget makes copies of the <code>pattern</code> nodes without knowing or caring which directives have been placed on them, or how many children are contained within the node. It then simply sets the <code>model=</code> attribute of each of the nodes to the correct index into the list. More about pattern directives is available in the <a href="viewindepth.html">Views</a> section.</p><p>In the above example, even though the <code>interests</code> list had been placed on the Model stack, we were able to access the <code>name</code> string without knowing its absolute path by using the relative path <code>../name</code>, and we were able to render the <code>interests</code> list with a different View Widget using the relative path <code>.</code>.</p><p>The output from generating the above HTML will look like this:</p><pre>
&lt;html&gt;
  &lt;div&gt;
    &lt;h3&gt;Donovan&lt;/h3&gt;
    &lt;p&gt;Computers&lt;/p&gt;
    &lt;p&gt;Music&lt;/p&gt;
    &lt;h6&gt;['Computers', 'Music']&lt;/h6&gt;
    &lt;/div&gt;
&lt;/html&gt;
</pre><h2>IModel Adapters<a name="auto3"></a></h2><a name="imodel"></a><p>The IModel interface is documented in <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.web.woven.interfaces.IModel.html" title="twisted.web.woven.interfaces.IModel">twisted.web.woven.interfaces.IModel</a></code>. It describes the interfaces Models must implement in order to participate in the Woven Model stack. If you are inheriting from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.web.woven.model.Model.html" title="twisted.web.woven.model.Model">twisted.web.woven.model.Model</a></code>, most of these interfaces will be implemented for you.</p><p>The interfaces that we will be most interested in implementing are those that are designed to be overridden for customization, getData and setData.</p><p>For example, we may wish to create a wrapper for some data which we will retrieve out of a SQL database. To do so, we create a subclass of Model:</p><pre>
class DatabaseJunk(model.Model):
    def getData(self, request):
        someCursor.execute(&quot;select * from foo&quot;)
        return someCursor.fetchall()
    
    def getSubmodel(self, request, name):
        row = self.getData(request)[int(name)]
        return RowUpdater(row)
  
class RowUpdater(model.Model):
    def __init__(self, id):
        self.row = row
   
    def getData(self, request):
        return self.row
    
    def setData(self, request, data):
        someCursor.execute(
        &quot;update foo set bar=%s, baz=%s where id = %s&quot;, 
        (data[0], data[1], self.row[0]))
</pre><p>The result of getData must be an IModel implementor, or may be a Deferred. Thus you may use the IModel interface to produce data from an adbapi call, a pb call, etc. When the data returned is a Deferred, Woven will pause rendering of the current node until the data is available.</p><h2>Registering an IModel adapter for a class<a name="auto4"></a></h2><p>Woven makes use of the twisted component system. Components, which are discussed in the <a href="components.html">Components</a> section, allow classes to declare that they implement a specific Interface for another class. This is useful if you already have classes in which you store data, and wish to create thin IModel adapter wrappers around them:</p><pre class="python">
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyData</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">something</span><span class="py-src-op">=</span><span class="py-src-string">&quot;&quot;</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">something</span> <span class="py-src-op">=</span> <span class="py-src-variable">something</span><span class="py-src-newline">
</span>    <span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">class</span> <span class="py-src-identifier">MyDataModel</span><span class="py-src-op">(</span><span class="py-src-parameter">models</span><span class="py-src-op">.</span><span class="py-src-parameter">MethodModel</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span>    <span class="py-src-comment">## When the MyDataModel adapter is wrapped around an instance
</span>    <span class="py-src-comment">## of MyData, the original MyData instance will be stored in 'orig'
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">wmfactory_something</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">request</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">orig</span><span class="py-src-op">.</span><span class="py-src-variable">something</span><span class="py-src-newline">
</span>    <span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">components</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">web</span><span class="py-src-op">.</span><span class="py-src-variable">woven</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">interfaces</span><span class="py-src-newline">
</span>    <span class="py-src-nl">
</span><span class="py-src-variable">components</span><span class="py-src-op">.</span><span class="py-src-variable">registerAdapter</span><span class="py-src-op">(</span><span class="py-src-variable">MyDataModel</span><span class="py-src-op">,</span> <span class="py-src-variable">MyData</span><span class="py-src-op">,</span> <span class="py-src-variable">interfaces</span><span class="py-src-op">.</span><span class="py-src-variable">IModel</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></pre><h2>Model Factories<a name="auto5"></a></h2><a name="modelfactories"></a><p>Using a separate Model class for each individual piece of data in the system makes sense when you are able to generalize your Model classes enough so they are reusable. However, it is often easier, especially if you need to perform highly varied SQL calls to produce your data, to use a Model which supports Model factories.</p><p>There are two ways to use Model factories. The first is to have a separate Model class which subclasses <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.web.woven.model.MethodModel.html" title="twisted.web.woven.model.MethodModel">model.MethodModel</a></code>. The second is to simply not pass any Model at all to the Page instance, in which case the Page itself will act as a MethodModel.</p><p>MethodModel classes should provide methods prefixed with <code>wmfactory_</code>, which will be called when the directive <code>model=</code> is present in a template. For example, given the node <code>&lt;div model=&quot;foo&quot; /&gt;</code>, a method named <code>wmfactory_foo</code> will be called:</p><pre class="python">
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyModel</span><span class="py-src-op">(</span><span class="py-src-parameter">model</span><span class="py-src-op">.</span><span class="py-src-parameter">MethodModel</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">wmfactory_foo</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">request</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-op">[</span><span class="py-src-string">'foo'</span><span class="py-src-op">,</span> <span class="py-src-string">'bar'</span><span class="py-src-op">,</span> <span class="py-src-string">'baz'</span><span class="py-src-op">]</span><span class="py-src-newline">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-endmarker"></span></pre><p>If you did not pass any Model object when you created your Page instance, the Page class will act as a MethodModel. Thus, you can place your <code>wmfactory_</code> methods directly on your Page subclass:</p><pre class="python">
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyPage</span><span class="py-src-op">(</span><span class="py-src-parameter">page</span><span class="py-src-op">.</span><span class="py-src-parameter">Page</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">wmfactory_foo</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">request</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-op">[</span><span class="py-src-string">'foo'</span><span class="py-src-op">,</span> <span class="py-src-string">'bar'</span><span class="py-src-op">,</span> <span class="py-src-string">'baz'</span><span class="py-src-op">]</span><span class="py-src-newline">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-endmarker"></span></pre><p>Model factories are a useful way to write some Python code which generates your page model data, for pages which need to look up or calculate data in some way.</p></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 1.3.0</span></body></html>