<!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"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> examples.versioned_rows.versioned_map — SQLAlchemy 1.2 Documentation </title> <!-- begin iterate through site-imported + sphinx environment css_files --> <link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../../../_static/docs.css" type="text/css" /> <link rel="stylesheet" href="../../../_static/changelog.css" type="text/css" /> <link rel="stylesheet" href="../../../_static/sphinx_paramlinks.css" type="text/css" /> <!-- end iterate through site-imported + sphinx environment css_files --> <!-- begin layout.mako headers --> <link rel="index" title="Index" href="../../../genindex.html" /> <link rel="search" title="Search" href="../../../search.html" /> <link rel="copyright" title="Copyright" href="../../../copyright.html" /> <link rel="top" title="SQLAlchemy 1.2 Documentation" href="../../../index.html" /> <link rel="up" title="Module code" href="../../index.html" /> <!-- end layout.mako headers --> </head> <body> <div id="docs-container"> <div id="docs-top-navigation-container" class="body-background"> <div id="docs-header"> <div id="docs-version-header"> Release: <span class="version-num">1.2.19</span> | Release Date: April 15, 2019 </div> <h1>SQLAlchemy 1.2 Documentation</h1> </div> </div> <div id="docs-body-container"> <div id="fixed-sidebar" class=""> <div id="index-nav"> <form class="search" action="../../../search.html" method="get"> <label> Search terms: <input type="text" placeholder="search..." name="q" size="12" /> </label> <input type="submit" value="Search" /> <input type="hidden" name="check_keywords" value="yes" /> <input type="hidden" name="area" value="default" /> </form> <p> <a href="../../../contents.html">Contents</a> | <a href="../../../genindex.html">Index</a> </p> </div> </div> <div id="docs-body" class="" > <h1>Source code for examples.versioned_rows.versioned_map</h1><div class="highlight"><pre> <span></span><span class="sd">"""A variant of the versioned_rows example built around the</span> <span class="sd">concept of a "vertical table" structure, like those illustrated in</span> <span class="sd">:ref:`examples_vertical_tables` examples.</span> <span class="sd">Here we store a dictionary of key/value pairs, storing the k/v's in a</span> <span class="sd">"vertical" fashion where each key gets a row. The value is split out</span> <span class="sd">into two separate datatypes, string and int - the range of datatype</span> <span class="sd">storage can be adjusted for individual needs.</span> <span class="sd">Changes to the "data" attribute of a ConfigData object result in the</span> <span class="sd">ConfigData object being copied into a new one, and new associations to</span> <span class="sd">its data are created. Values which aren't changed between versions are</span> <span class="sd">referenced by both the former and the newer ConfigData object.</span> <span class="sd">Overall, only INSERT statements are emitted - no rows are UPDATed or</span> <span class="sd">DELETEd.</span> <span class="sd">An optional feature is also illustrated which associates individual</span> <span class="sd">key/value pairs with the ConfigData object in which it first</span> <span class="sd">originated. Since a new row is only persisted when a new value is</span> <span class="sd">created for a particular key, the recipe provides a way to query among</span> <span class="sd">the full series of changes which occurred for any particular key in</span> <span class="sd">the dictionary.</span> <span class="sd">The set of all ConfigData in a particular table represents a single</span> <span class="sd">series of versions. By adding additional columns to ConfigData, the</span> <span class="sd">system can be made to store multiple version streams distinguished by</span> <span class="sd">those additional values.</span> <span class="sd">"""</span> <span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="k">import</span> <span class="n">Column</span> <span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="k">import</span> <span class="n">create_engine</span> <span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="k">import</span> <span class="n">event</span> <span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="k">import</span> <span class="n">ForeignKey</span> <span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="k">import</span> <span class="n">Integer</span> <span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="k">import</span> <span class="n">String</span> <span class="kn">from</span> <span class="nn">sqlalchemy.ext.associationproxy</span> <span class="k">import</span> <span class="n">association_proxy</span> <span class="kn">from</span> <span class="nn">sqlalchemy.ext.declarative</span> <span class="k">import</span> <span class="n">declarative_base</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">attributes</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">backref</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">make_transient</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">relationship</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">Session</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">sessionmaker</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="k">import</span> <span class="n">validates</span> <span class="kn">from</span> <span class="nn">sqlalchemy.orm.collections</span> <span class="k">import</span> <span class="n">attribute_mapped_collection</span> <span class="nd">@event</span><span class="o">.</span><span class="n">listens_for</span><span class="p">(</span><span class="n">Session</span><span class="p">,</span> <span class="s2">"before_flush"</span><span class="p">)</span> <span class="k">def</span> <span class="nf">before_flush</span><span class="p">(</span><span class="n">session</span><span class="p">,</span> <span class="n">flush_context</span><span class="p">,</span> <span class="n">instances</span><span class="p">):</span> <span class="sd">"""Apply the new_version() method of objects which are</span> <span class="sd"> marked as dirty during a flush.</span> <span class="sd"> """</span> <span class="k">for</span> <span class="n">instance</span> <span class="ow">in</span> <span class="n">session</span><span class="o">.</span><span class="n">dirty</span><span class="p">:</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="s2">"new_version"</span><span class="p">)</span> <span class="ow">and</span> <span class="n">session</span><span class="o">.</span><span class="n">is_modified</span><span class="p">(</span> <span class="n">instance</span><span class="p">,</span> <span class="n">passive</span><span class="o">=</span><span class="kc">True</span> <span class="p">):</span> <span class="c1"># make it transient</span> <span class="n">instance</span><span class="o">.</span><span class="n">new_version</span><span class="p">(</span><span class="n">session</span><span class="p">)</span> <span class="c1"># re-add</span> <span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span> <span class="n">Base</span> <span class="o">=</span> <span class="n">declarative_base</span><span class="p">()</span> <span class="k">class</span> <span class="nc">ConfigData</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span> <span class="sd">"""Represent a series of key/value pairs.</span> <span class="sd"> ConfigData will generate a new version of itself</span> <span class="sd"> upon change.</span> <span class="sd"> The "data" dictionary provides access via</span> <span class="sd"> string name mapped to a string/int value.</span> <span class="sd"> """</span> <span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"config"</span> <span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="sd">"""Primary key column of this ConfigData."""</span> <span class="n">elements</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span> <span class="s2">"ConfigValueAssociation"</span><span class="p">,</span> <span class="n">collection_class</span><span class="o">=</span><span class="n">attribute_mapped_collection</span><span class="p">(</span><span class="s2">"name"</span><span class="p">),</span> <span class="n">backref</span><span class="o">=</span><span class="n">backref</span><span class="p">(</span><span class="s2">"config_data"</span><span class="p">),</span> <span class="n">lazy</span><span class="o">=</span><span class="s2">"subquery"</span><span class="p">,</span> <span class="p">)</span> <span class="sd">"""Dictionary-backed collection of ConfigValueAssociation objects,</span> <span class="sd"> keyed to the name of the associated ConfigValue.</span> <span class="sd"> Note there's no "cascade" here. ConfigValueAssociation objects</span> <span class="sd"> are never deleted or changed.</span> <span class="sd"> """</span> <span class="k">def</span> <span class="nf">_new_value</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="sd">"""Create a new entry for usage in the 'elements' dictionary."""</span> <span class="k">return</span> <span class="n">ConfigValueAssociation</span><span class="p">(</span><span class="n">ConfigValue</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">))</span> <span class="n">data</span> <span class="o">=</span> <span class="n">association_proxy</span><span class="p">(</span><span class="s2">"elements"</span><span class="p">,</span> <span class="s2">"value"</span><span class="p">,</span> <span class="n">creator</span><span class="o">=</span><span class="n">_new_value</span><span class="p">)</span> <span class="sd">"""Proxy to the 'value' elements of each related ConfigValue,</span> <span class="sd"> via the 'elements' dictionary.</span> <span class="sd"> """</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span> <span class="nd">@validates</span><span class="p">(</span><span class="s2">"elements"</span><span class="p">)</span> <span class="k">def</span> <span class="nf">_associate_with_element</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">element</span><span class="p">):</span> <span class="sd">"""Associate incoming ConfigValues with this</span> <span class="sd"> ConfigData, if not already associated.</span> <span class="sd"> This is an optional feature which allows</span> <span class="sd"> more comprehensive history tracking.</span> <span class="sd"> """</span> <span class="k">if</span> <span class="n">element</span><span class="o">.</span><span class="n">config_value</span><span class="o">.</span><span class="n">originating_config</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="n">element</span><span class="o">.</span><span class="n">config_value</span><span class="o">.</span><span class="n">originating_config</span> <span class="o">=</span> <span class="bp">self</span> <span class="k">return</span> <span class="n">element</span> <span class="k">def</span> <span class="nf">new_version</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">session</span><span class="p">):</span> <span class="c1"># convert to an INSERT</span> <span class="n">make_transient</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># history of the 'elements' collection.</span> <span class="c1"># this is a tuple of groups: (added, unchanged, deleted)</span> <span class="n">hist</span> <span class="o">=</span> <span class="n">attributes</span><span class="o">.</span><span class="n">get_history</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"elements"</span><span class="p">)</span> <span class="c1"># rewrite the 'elements' collection</span> <span class="c1"># from scratch, removing all history</span> <span class="n">attributes</span><span class="o">.</span><span class="n">set_committed_value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"elements"</span><span class="p">,</span> <span class="p">{})</span> <span class="c1"># new elements in the "added" group</span> <span class="c1"># are moved to our new collection.</span> <span class="k">for</span> <span class="n">elem</span> <span class="ow">in</span> <span class="n">hist</span><span class="o">.</span><span class="n">added</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">elements</span><span class="p">[</span><span class="n">elem</span><span class="o">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">elem</span> <span class="c1"># copy elements in the 'unchanged' group.</span> <span class="c1"># the new ones associate with the new ConfigData,</span> <span class="c1"># the old ones stay associated with the old ConfigData</span> <span class="k">for</span> <span class="n">elem</span> <span class="ow">in</span> <span class="n">hist</span><span class="o">.</span><span class="n">unchanged</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">elements</span><span class="p">[</span><span class="n">elem</span><span class="o">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">ConfigValueAssociation</span><span class="p">(</span> <span class="n">elem</span><span class="o">.</span><span class="n">config_value</span> <span class="p">)</span> <span class="c1"># we also need to expire changes on each ConfigValueAssociation</span> <span class="c1"># that is to remain associated with the old ConfigData.</span> <span class="c1"># Here, each one takes care of that in its new_version()</span> <span class="c1"># method, though we could do that here as well.</span> <span class="k">class</span> <span class="nc">ConfigValueAssociation</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span> <span class="sd">"""Relate ConfigData objects to associated ConfigValue objects."""</span> <span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"config_value_association"</span> <span class="n">config_id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"config.id"</span><span class="p">),</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="sd">"""Reference the primary key of the ConfigData object."""</span> <span class="n">config_value_id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"config_value.id"</span><span class="p">),</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="sd">"""Reference the primary key of the ConfigValue object."""</span> <span class="n">config_value</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="s2">"ConfigValue"</span><span class="p">,</span> <span class="n">lazy</span><span class="o">=</span><span class="s2">"joined"</span><span class="p">,</span> <span class="n">innerjoin</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="sd">"""Reference the related ConfigValue object."""</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">config_value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">config_value</span> <span class="o">=</span> <span class="n">config_value</span> <span class="k">def</span> <span class="nf">new_version</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">session</span><span class="p">):</span> <span class="sd">"""Expire all pending state, as ConfigValueAssociation is immutable."""</span> <span class="n">session</span><span class="o">.</span><span class="n">expire</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">config_value</span><span class="o">.</span><span class="n">name</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">config_value</span><span class="o">.</span><span class="n">value</span> <span class="nd">@value</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="sd">"""Intercept set events.</span> <span class="sd"> Create a new ConfigValueAssociation upon change,</span> <span class="sd"> replacing this one in the parent ConfigData's dictionary.</span> <span class="sd"> If no net change, do nothing.</span> <span class="sd"> """</span> <span class="k">if</span> <span class="n">value</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">config_value</span><span class="o">.</span><span class="n">value</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">config_data</span><span class="o">.</span><span class="n">elements</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">ConfigValueAssociation</span><span class="p">(</span> <span class="n">ConfigValue</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">config_value</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="p">)</span> <span class="k">class</span> <span class="nc">ConfigValue</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span> <span class="sd">"""Represent an individual key/value pair at a given point in time.</span> <span class="sd"> ConfigValue is immutable.</span> <span class="sd"> """</span> <span class="n">__tablename__</span> <span class="o">=</span> <span class="s2">"config_value"</span> <span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="n">name</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="n">originating_config_id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span> <span class="n">Integer</span><span class="p">,</span> <span class="n">ForeignKey</span><span class="p">(</span><span class="s2">"config.id"</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span> <span class="p">)</span> <span class="n">int_value</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">)</span> <span class="n">string_value</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">(</span><span class="mi">255</span><span class="p">))</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span> <span class="n">originating_config</span> <span class="o">=</span> <span class="n">relationship</span><span class="p">(</span><span class="s2">"ConfigData"</span><span class="p">)</span> <span class="sd">"""Reference to the originating ConfigData.</span> <span class="sd"> This is optional, and allows history tracking of</span> <span class="sd"> individual values.</span> <span class="sd"> """</span> <span class="k">def</span> <span class="nf">new_version</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">session</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">(</span><span class="s2">"ConfigValue is immutable."</span><span class="p">)</span> <span class="nd">@property</span> <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"int_value"</span><span class="p">,</span> <span class="s2">"string_value"</span><span class="p">):</span> <span class="n">v</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span> <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> <span class="k">return</span> <span class="n">v</span> <span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="kc">None</span> <span class="nd">@value</span><span class="o">.</span><span class="n">setter</span> <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">int</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">int_value</span> <span class="o">=</span> <span class="n">value</span> <span class="bp">self</span><span class="o">.</span><span class="n">string_value</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">else</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">string_value</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">int_value</span> <span class="o">=</span> <span class="kc">None</span> <span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span><span class="s2">"sqlite://"</span><span class="p">,</span> <span class="n">echo</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="n">Base</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">create_all</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span> <span class="n">Session</span> <span class="o">=</span> <span class="n">sessionmaker</span><span class="p">(</span><span class="n">engine</span><span class="p">)</span> <span class="n">sess</span> <span class="o">=</span> <span class="n">Session</span><span class="p">()</span> <span class="n">config</span> <span class="o">=</span> <span class="n">ConfigData</span><span class="p">(</span> <span class="p">{</span><span class="s2">"user_name"</span><span class="p">:</span> <span class="s2">"twitter"</span><span class="p">,</span> <span class="s2">"hash_id"</span><span class="p">:</span> <span class="s2">"4fedffca37eaf"</span><span class="p">,</span> <span class="s2">"x"</span><span class="p">:</span> <span class="mi">27</span><span class="p">,</span> <span class="s2">"y"</span><span class="p">:</span> <span class="mi">450</span><span class="p">}</span> <span class="p">)</span> <span class="n">sess</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">config</span><span class="p">)</span> <span class="n">sess</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> <span class="n">version_one</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">id</span> <span class="n">config</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="s2">"user_name"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"yahoo"</span> <span class="n">sess</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> <span class="n">version_two</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="n">id</span> <span class="k">assert</span> <span class="n">version_one</span> <span class="o">!=</span> <span class="n">version_two</span> <span class="c1"># two versions have been created.</span> <span class="k">assert</span> <span class="n">config</span><span class="o">.</span><span class="n">data</span> <span class="o">==</span> <span class="p">{</span> <span class="s2">"user_name"</span><span class="p">:</span> <span class="s2">"yahoo"</span><span class="p">,</span> <span class="s2">"hash_id"</span><span class="p">:</span> <span class="s2">"4fedffca37eaf"</span><span class="p">,</span> <span class="s2">"x"</span><span class="p">:</span> <span class="mi">27</span><span class="p">,</span> <span class="s2">"y"</span><span class="p">:</span> <span class="mi">450</span><span class="p">,</span> <span class="p">}</span> <span class="n">old_config</span> <span class="o">=</span> <span class="n">sess</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">ConfigData</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">version_one</span><span class="p">)</span> <span class="k">assert</span> <span class="n">old_config</span><span class="o">.</span><span class="n">data</span> <span class="o">==</span> <span class="p">{</span> <span class="s2">"user_name"</span><span class="p">:</span> <span class="s2">"twitter"</span><span class="p">,</span> <span class="s2">"hash_id"</span><span class="p">:</span> <span class="s2">"4fedffca37eaf"</span><span class="p">,</span> <span class="s2">"x"</span><span class="p">:</span> <span class="mi">27</span><span class="p">,</span> <span class="s2">"y"</span><span class="p">:</span> <span class="mi">450</span><span class="p">,</span> <span class="p">}</span> <span class="c1"># the history of any key can be acquired using</span> <span class="c1"># the originating_config_id attribute</span> <span class="n">history</span> <span class="o">=</span> <span class="p">(</span> <span class="n">sess</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">ConfigValue</span><span class="p">)</span> <span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">ConfigValue</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"user_name"</span><span class="p">)</span> <span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">ConfigValue</span><span class="o">.</span><span class="n">originating_config_id</span><span class="p">)</span> <span class="o">.</span><span class="n">all</span><span class="p">()</span> <span class="p">)</span> <span class="k">assert</span> <span class="p">[(</span><span class="n">h</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">h</span><span class="o">.</span><span class="n">originating_config_id</span><span class="p">)</span> <span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">history</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span> <span class="p">[(</span><span class="s2">"twitter"</span><span class="p">,</span> <span class="n">version_one</span><span class="p">),</span> <span class="p">(</span><span class="s2">"yahoo"</span><span class="p">,</span> <span class="n">version_two</span><span class="p">)]</span> <span class="p">)</span></pre></div> </div> </div> <div id="docs-bottom-navigation" class="docs-navigation-links"> <div id="docs-copyright"> © <a href="../../../copyright.html">Copyright</a> 2007-2019, the SQLAlchemy authors and contributors. Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.0.1. </div> </div> </div> <script type="text/javascript"> var DOCUMENTATION_OPTIONS = { URL_ROOT: '../../../', VERSION: '1.2.19', COLLAPSE_MODINDEX: false, FILE_SUFFIX: '.html' }; </script> <script type="text/javascript" id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script> <!-- begin iterate through sphinx environment script_files --> <script type="text/javascript" src="../../../_static/jquery.js"></script> <script type="text/javascript" src="../../../_static/underscore.js"></script> <script type="text/javascript" src="../../../_static/doctools.js"></script> <script type="text/javascript" src="../../../_static/language_data.js"></script> <!-- end iterate through sphinx environment script_files --> <script type="text/javascript" src="../../../_static/detectmobile.js"></script> <script type="text/javascript" src="../../../_static/init.js"></script> </body> </html>