Permalink
Browse files

updated docs, tests, misc

  • Loading branch information...
1 parent 52f32c1 commit f78b27dc1a06b5b13f17804d286e405f1b85fd73 @bconstantin bconstantin committed Oct 31, 2010
Showing with 729 additions and 424 deletions.
  1. +62 −22 CHANGES.html
  2. +71 −20 CHANGES.rst
  3. +216 −166 DOCS.html
  4. +197 −116 DOCS.rst
  5. +68 −32 README.html
  6. +83 −52 README.rst
  7. +4 −0 pexp/management/commands/p2cmd.py
  8. +1 −1 pexp/management/commands/polybench.py
  9. +0 −2 pexp/models.py
  10. +6 −7 polymorphic/polymorphic_model.py
  11. +2 −1 polymorphic/tests.py
  12. +9 −4 rst-to-html.py
  13. +10 −1 rst.css
View
84 CHANGES.html
@@ -11,6 +11,10 @@
color: #47c;
}
+h1 { padding-top: 15px; }
+h2 { padding-top: 10px; }
+h3 { padding-top: 7px; }
+
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
@@ -216,11 +220,54 @@
</div>
<div class="section" id="changelog">
<h1>Changelog</h1>
-<p>.</p>
+<div class="section" id="v1-0-beta-2">
+<h2>2010-10-25 V1.0 Beta 2</h2>
+<div class="section" id="this-is-a-v1-0-beta-testing-release">
+<h3>This is a V1.0 Beta/Testing Release</h3>
+<p>The release contains a considerable amount of changes in some of the more
+critical parts of the software. It's intended for testing and development
+environments and not for production environments. For these, it's best to
+wait a few weeks for the proper V1.0 release, to allow some time for any
+potential problems to show up (if they exist).</p>
+</div>
+<div class="section" id="new-features-and-api-changes-since-beta-1">
+<h3>New Features and API changes since Beta 1</h3>
+<ul class="simple">
+<li>API CHANGE: <tt class="docutils literal">.extra()</tt> has been re-implemented. Now it's polymorphic by
+default and works (nearly) without restrictions (please see docs). This is an
+incompatible API change regarding previous versions of django_polymorphic.
+Support for the <tt class="docutils literal">polymorphic</tt> keyword parameter has been removed.
+You can get back the non-polymorphic behaviour by using
+<tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra()</span></tt>.</li>
+</ul>
+<ul class="simple">
+<li><tt class="docutils literal">.non_polymorphic()</tt> queryset member function added. This is preferable to
+using <tt class="docutils literal"><span class="pre">.base_objects...</span></tt>, as it just makes the resulting queryset non-polymorphic
+and does not change anything else in the behaviour of the manager used (while
+<tt class="docutils literal">.base_objects</tt> is just a different manager).</li>
+</ul>
+<ul>
+<li><p class="first"><tt class="docutils literal">.get_real_instances()</tt>: implementation modified to allow the following
+more simple and intuitive use:</p>
+<pre class="literal-block">
+&gt;&gt;&gt; qs = ModelA.objects.all().non_polymorphic()
+&gt;&gt;&gt; qs.get_real_instances()
+</pre>
+<p>which is equivalent to:</p>
+<pre class="literal-block">
+&gt;&gt;&gt; ModelA.objects.all()
+</pre>
+</li>
+</ul>
+<ul class="simple">
+<li>misc changes/improvements</li>
+</ul>
+</div>
+</div>
<hr class="docutils" />
+<div class="section" id="v1-0-beta-1">
+<h2>2010-10-18 V1.0 Beta 1</h2>
<div class="section" id="id1">
-<h2>2010-10-18</h2>
-<div class="section" id="this-is-a-v1-0-beta-testing-release">
<h3>This is a V1.0 Beta/Testing Release</h3>
<p>This release is mostly a cleanup and maintenance release that also
improves a number of minor things and fixes one (non-critical) bug.</p>
@@ -233,7 +280,7 @@
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
-potential problems to turn up (if they exist).</p>
+potential problems to show up (if they exist).</p>
<p>If you encounter any such problems, please post them in the discussion group
or open an issue on GitHub or BitBucket (or send me an email).</p>
<p>There also have been a number of minor API changes.
@@ -245,15 +292,14 @@
<li><p class="first">official Django 1.3 alpha compatibility</p>
</li>
<li><p class="first"><tt class="docutils literal">PolymorphicModel.__getattribute__</tt> hack removed.
-The python __getattribute__ hack generally causes a considerable
-overhead and to have this in the performance-sensitive PolymorphicModel
-class was somewhat problematic. It's gone for good now.</p>
+This improves performance considerably as python's __getattribute__
+generally causes a pretty large processing overhead. It's gone now.</p>
</li>
-<li><p class="first"><tt class="docutils literal">polymorphic_dumpdata</tt> management command functionality removed:
-The regular Django dumpdata command now automatically works correctly
-for polymorphic models with all Django versions.</p>
+<li><p class="first">the <tt class="docutils literal">polymorphic_dumpdata</tt> management command is not needed anymore
+and has been disabled, as the regular Django dumpdata command now automatically
+works correctly with polymorphic models (for all supported versions of Django).</p>
</li>
-<li><p class="first">.get_real_instances() has been elevated to an official part of the API:</p>
+<li><p class="first"><tt class="docutils literal">.get_real_instances()</tt> has been elevated to an official part of the API:</p>
<pre class="literal-block">
real_objects = ModelA.objects.get_real_instances(base_objects_list_or_queryset)
</pre>
@@ -278,14 +324,11 @@
</div>
<div class="section" id="api-changes">
<h3>API Changes</h3>
-<div class="section" id="polymorphic-dumpdata">
-<h4>polymorphic_dumpdata</h4>
-<p>The polymorphic_dumpdata management command is not needed anymore
-and has been removed, as the regular Django dumpdata command now automatically
+<p><strong>polymorphic_dumpdata</strong></p>
+<p>The management command <tt class="docutils literal">polymorphic_dumpdata</tt> is not needed anymore
+and has been disabled, as the regular Django dumpdata command now automatically
works correctly with polymorphic models (for all supported versions of Django).</p>
-</div>
-<div class="section" id="output-of-queryset-or-object-printing">
-<h4>Output of Queryset or Object Printing</h4>
+<p><strong>Output of Queryset or Object Printing</strong></p>
<p>In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
To get the old behaviour when printing querysets, you need to replace your model definition:</p>
@@ -303,14 +346,11 @@
<blockquote>
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
<p>(the old ones still exist for compatibility)</p>
-</div>
-<div class="section" id="running-the-test-suite-with-django-1-3">
-<h4>Running the Test suite with Django 1.3</h4>
+<p><strong>Running the Test suite with Django 1.3</strong></p>
<p>Django 1.3 requires <tt class="docutils literal">python manage.py test polymorphic</tt> instead of
just <tt class="docutils literal">python manage.py test</tt>.</p>
</div>
</div>
-</div>
<hr class="docutils" />
<div class="section" id="id2">
<h2>2010-2-22</h2>
View
91 CHANGES.rst
@@ -3,12 +3,67 @@
Changelog
++++++++++
-.
+2010-11-01 V1.0 Beta 2
+======================
+
+This is a V1.0 Beta/Testing Release
+-----------------------------------
+
+Beta 2 accumulated somewhat more changes than intended. It's still
+intended for testing and development environments and not for production
+(it's best to wait for the final V1.0 for production servers).
+
+New Features and API changes since Beta 1
+-----------------------------------------
+
+* API CHANGE: ``.extra()`` has been re-implemented. Now it's polymorphic by
+ default and works (nearly) without restrictions (please see docs). This is an
+ incompatible API change regarding previous versions of django_polymorphic.
+ Support for the ``polymorphic`` keyword parameter has been removed.
+ You can get back the non-polymorphic behaviour by using
+ ``ModelA.objects.non_polymorphic().extra(...)``.
+
+* API CHANGE: ``ShowFieldContent`` and ``ShowFieldTypeAndContent`` now
+ use a slightly different output format. If this causes too much trouble for
+ your test cases, you can get the old behaviour back (mostly) by adding
+ ``polymorphic_showfield_old_format = True`` to your model definitions.
+ ``ShowField...`` also produces more informative output for custom
+ primary keys.
+
+* ``.non_polymorphic()`` queryset member function added. This is preferable to
+ using ``.base_objects...``, as it just makes the resulting queryset non-polymorphic
+ and does not change anything else in the behaviour of the manager used (while
+ ``.base_objects`` is just a different manager).
+
+* ``.get_real_instances()``: implementation modified to allow the following
+ more simple and intuitive use::
+
+ >>> qs = ModelA.objects.all().non_polymorphic()
+ >>> qs.get_real_instances()
+
+ which is equivalent to::
+
+ >>> ModelA.objects.all()
+
+* misc changes/improvements
+
+Bugfixes
+------------------------
+
+* Custom fields could cause problems when used as the primary key.
+ In derived models, Django's automatic ".pk" field does not always work
+ correctly for such custom fields: "some_object.pk" and "some_object.id"
+ return different results (which they shouldn't, as pk should always be just
+ an alias for the primary key field). It's unclear yet if the problem lies in
+ Django or the affected custom fields. Regardless, the problem resulting
+ from this has been fixed with a small workaround.
+ "python manage.py test polymorphic" also tests and reports on this problem now.
+
------------------------------------------------------------------
-2010-10-18
-==========
+2010-10-18 V1.0 Beta 1
+======================
This is a V1.0 Beta/Testing Release
-----------------------------------
@@ -27,7 +82,7 @@ The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
-potential problems to turn up (if they exist).
+potential problems to show up (if they exist).
If you encounter any such problems, please post them in the discussion group
or open an issue on GitHub or BitBucket (or send me an email).
@@ -41,15 +96,14 @@ New Features
* official Django 1.3 alpha compatibility
* ``PolymorphicModel.__getattribute__`` hack removed.
- The python __getattribute__ hack generally causes a considerable
- overhead and to have this in the performance-sensitive PolymorphicModel
- class was somewhat problematic. It's gone for good now.
+ This improves performance considerably as python's __getattribute__
+ generally causes a pretty large processing overhead. It's gone now.
-* ``polymorphic_dumpdata`` management command functionality removed:
- The regular Django dumpdata command now automatically works correctly
- for polymorphic models with all Django versions.
+* the ``polymorphic_dumpdata`` management command is not needed anymore
+ and has been disabled, as the regular Django dumpdata command now automatically
+ works correctly with polymorphic models (for all supported versions of Django).
-* .get_real_instances() has been elevated to an official part of the API::
+* ``.get_real_instances()`` has been elevated to an official part of the API::
real_objects = ModelA.objects.get_real_instances(base_objects_list_or_queryset)
@@ -66,21 +120,19 @@ New Features
Bugfixes
------------------------
-* removed requirement for primary key to be an IntegerField.
+* Removed requirement for primary key to be an IntegerField.
Thanks to Mathieu Steele and Malthe Borch.
API Changes
-----------
-polymorphic_dumpdata
-####################
+**polymorphic_dumpdata**
-The polymorphic_dumpdata management command is not needed anymore
-and has been removed, as the regular Django dumpdata command now automatically
+The management command ``polymorphic_dumpdata`` is not needed anymore
+and has been disabled, as the regular Django dumpdata command now automatically
works correctly with polymorphic models (for all supported versions of Django).
-Output of Queryset or Object Printing
-#####################################
+**Output of Queryset or Object Printing**
In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
@@ -102,8 +154,7 @@ are now:
(the old ones still exist for compatibility)
-Running the Test suite with Django 1.3
-######################################
+**Running the Test suite with Django 1.3**
Django 1.3 requires ``python manage.py test polymorphic`` instead of
just ``python manage.py test``.
View
382 DOCS.html
@@ -7,13 +7,22 @@
<title></title>
<style type="text/css">
-h1, h2, h3, h4 {
+h1, h2, h3, h4,
+#table-of-contents
+{
color: #47c;
}
+h1 { padding-top: 15px; }
+h2 { padding-top: 10px; }
+h3 { padding-top: 7px; }
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
+li {
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
tt {
color: #080;
@@ -218,22 +227,23 @@
<ul class="simple">
<li><a class="reference internal" href="#polymorphic-models-for-django" id="id1">Polymorphic Models for Django</a></li>
<li><a class="reference internal" href="#quickstart" id="id2">Quickstart</a></li>
-<li><a class="reference internal" href="#more-about-installation-testing" id="id3">More about Installation / Testing</a></li>
-<li><a class="reference internal" href="#more-polymorphic-functionality" id="id4">More Polymorphic Functionality</a></li>
-<li><a class="reference internal" href="#custom-managers-querysets-manager-inheritance" id="id5">Custom Managers, Querysets &amp; Manager Inheritance</a></li>
-<li><a class="reference internal" href="#performance-considerations" id="id6">Performance Considerations</a></li>
-<li><a class="reference internal" href="#possible-optimizations" id="id7">Possible Optimizations</a></li>
-<li><a class="reference internal" href="#restrictions-caveats" id="id8">Restrictions &amp; Caveats</a></li>
-<li><a class="reference internal" href="#project-status" id="id9">Project Status</a></li>
-<li><a class="reference internal" href="#links" id="id10">Links</a></li>
+<li><a class="reference internal" href="#list-of-features" id="id3">List of Features</a></li>
+<li><a class="reference internal" href="#more-about-installation-testing" id="id4">More about Installation / Testing</a></li>
+<li><a class="reference internal" href="#more-polymorphic-functionality" id="id5">More Polymorphic Functionality</a></li>
+<li><a class="reference internal" href="#custom-managers-querysets-manager-inheritance" id="id6">Custom Managers, Querysets &amp; Manager Inheritance</a></li>
+<li><a class="reference internal" href="#performance-considerations" id="id7">Performance Considerations</a></li>
+<li><a class="reference internal" href="#possible-optimizations" id="id8">Possible Optimizations</a></li>
+<li><a class="reference internal" href="#restrictions-caveats" id="id9">Restrictions &amp; Caveats</a></li>
+<li><a class="reference internal" href="#project-status" id="id10">Project Status</a></li>
+<li><a class="reference internal" href="#links" id="id11">Links</a></li>
</ul>
</div>
</div>
<div class="section" id="quickstart">
<h1><a class="toc-backref" href="#id2">Quickstart</a></h1>
<div class="section" id="install">
<h2>Install</h2>
-<p>After uncompressing (if necessary), in the directory &quot;django_polymorphic&quot;,
+<p>After uncompressing (if necessary), in the directory &quot;...django_polymorphic&quot;,
execute (on Unix-like systems):</p>
<pre class="literal-block">
sudo python setup.py install
@@ -268,57 +278,66 @@
<h2>Get polymorphic query results</h2>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.all()
-[ &lt;Project: id 1, topic: &quot;John's Gathering&quot;&gt;,
- &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
- &lt;ResearchProject: id 3, topic: &quot;Swallow Aerodynamics&quot;, supervisor: &quot;Dr. Winter&quot;&gt; ]
+[ &lt;Project: id 1, topic &quot;John's Gathering&quot;&gt;,
+ &lt;ArtProject: id 2, topic &quot;Painting with Tim&quot;, artist &quot;T. Turner&quot;&gt;,
+ &lt;ResearchProject: id 3, topic &quot;Swallow Aerodynamics&quot;, supervisor &quot;Dr. Winter&quot;&gt; ]
</pre>
-<p>using instance_of and not_instance_of for narrowing the result to specific subtypes:</p>
+<p>use <tt class="docutils literal">instance_of</tt> or <tt class="docutils literal">not_instance_of</tt> for narrowing the result to specific subtypes:</p>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.instance_of(ArtProject)
-[ &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt; ]
+[ &lt;ArtProject: id 2, topic &quot;Painting with Tim&quot;, artist &quot;T. Turner&quot;&gt; ]
</pre>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
-[ &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
- &lt;ResearchProject: id 3, topic: &quot;Swallow Aerodynamics&quot;, supervisor: &quot;Dr. Winter&quot;&gt; ]
+[ &lt;ArtProject: id 2, topic &quot;Painting with Tim&quot;, artist &quot;T. Turner&quot;&gt;,
+ &lt;ResearchProject: id 3, topic &quot;Swallow Aerodynamics&quot;, supervisor &quot;Dr. Winter&quot;&gt; ]
</pre>
-<p>Polymorphic filtering: Let's get all projects where Mr. Turner is involved as an artist
+<p>Polymorphic filtering: Get all projects where Mr. Turner is involved as an artist
or supervisor (note the three underscores):</p>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
-[ &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
- &lt;ResearchProject: id 3, topic: &quot;History of Sculpting&quot;, supervisor: &quot;T. Turner&quot;&gt; ]
-</pre>
-</div>
-<div class="section" id="what-s-more">
-<h2>What's More?</h2>
-<p>Most of Django's standard ORM functionality is available and works as expected.
-ForeignKeys, ManyToManyFields and OneToToneFields to your polymorphic
-models work as shey should (polymorphic).</p>
-<p>In short, with django_polymorphic the Django models are much more &quot;pythonic&quot;, i.e.
-they just work as you expect them to work: very similar to ordinary python classes
-(which is not the case with vanilla Django model inheritance).</p>
+[ &lt;ArtProject: id 2, topic &quot;Painting with Tim&quot;, artist &quot;T. Turner&quot;&gt;,
+ &lt;ResearchProject: id 4, topic &quot;Color Use in Late Cubism&quot;, supervisor &quot;T. Turner&quot;&gt; ]
+</pre>
+<p>This is basically all you need to know, as django_polymorphic mostly
+works fully automatic and just delivers the expected (&quot;pythonic&quot;) results.</p>
<p>Note: In all example output, above and below, for a nicer and more informative
output the <cite>ShowFieldType</cite> mixin has been used (documented below).</p>
</div>
</div>
+<div class="section" id="list-of-features">
+<h1><a class="toc-backref" href="#id3">List of Features</a></h1>
+<ul class="simple">
+<li>Fully automatic; generally makes sure that the same objects are returned
+from the database that were stored there, regardless how they are retrieved</li>
+<li>Full support for ForeignKeys, ManyToManyFields and OneToToneFields</li>
+<li>Filtering for classes, equivalent to python's isinstance(): instance_of(...), not_instance_of(...)</li>
+<li>Polymorphic filtering/ordering etc., allowing the use of fields of derived models (&quot;ArtProject___artist&quot;)</li>
+<li>Support for user-defined custom managers</li>
+<li>Automatic inheritance of custom managers</li>
+<li>Support for user-defined custom queryset classes</li>
+<li>Non-polymorphic queries, if needed - with no other change in features/behaviour</li>
+<li>Combining querysets of different types/models (&quot;qs3 = qs1 | qs2&quot;)</li>
+<li>Nice/informative display of polymorphic queryset results</li>
+</ul>
+</div>
<div class="section" id="more-about-installation-testing">
-<h1><a class="toc-backref" href="#id3">More about Installation / Testing</a></h1>
+<h1><a class="toc-backref" href="#id4">More about Installation / Testing</a></h1>
<div class="section" id="requirements">
<h2>Requirements</h2>
-<p>Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
-on Django 1.1.1 / 1.2 beta and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
+<p>Django 1.1 (or later) and Python 2.4 or later. This code has been tested
+on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
</div>
<div class="section" id="included-test-suite">
<h2>Included Test Suite</h2>
<p>The repository (or tar file) contains a complete Django project
that may be used for tests or experiments, without any installation needed.</p>
-<p>To run the included test suite, execute:</p>
+<p>To run the included test suite, in the directory &quot;...django_polymorphic&quot; execute:</p>
<pre class="literal-block">
./manage test polymorphic
</pre>
-<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used for
-experiments - modify this file (pexp/management/commands/pcmd.py)
+<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used
+for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
to your liking, then run:</p>
<pre class="literal-block">
./manage syncdb # db is created in /var/tmp/... (settings.py)
@@ -327,19 +346,18 @@
</div>
<div class="section" id="installation">
<h2>Installation</h2>
-<p>In the directory &quot;django_polymorphic&quot;, execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
-<p>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> directory
+<p>In the directory &quot;...django_polymorphic&quot;, execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
+<p>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> subdirectory
(under &quot;django_polymorphic&quot;) into your Django project dir
(e.g. if you want to distribute your project with more 'batteries included').</p>
-<p>If you want to use the management command <tt class="docutils literal">polymorphic_dumpdata</tt>, then
-you need to add <tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting. This is also
-needed if you want to run the test cases in <cite>polymorphic/tests.py</cite>.</p>
-<p>In any case, Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
+<p>If you want to run the test cases in <cite>polymorphic/tests.py</cite>, you need to add
+<tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting.</p>
+<p>Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
needs to be listed in INSTALLED_APPS (usually it already is).</p>
</div>
</div>
<div class="section" id="more-polymorphic-functionality">
-<h1><a class="toc-backref" href="#id4">More Polymorphic Functionality</a></h1>
+<h1><a class="toc-backref" href="#id5">More Polymorphic Functionality</a></h1>
<p>In the examples below, these models are being used:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel
@@ -383,12 +401,12 @@
&lt;ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)&gt; ]
</pre>
</div>
-<div class="section" id="combining-querysets-of-different-types-models">
-<h2>Combining Querysets of different types/models</h2>
-<p>Querysets may now be regarded as object containers that allow the
-aggregation of different object types - very similar to python
-lists (as long as the objects are accessed through the manager of
-a common base class):</p>
+<div class="section" id="combining-querysets-querysets-as-object-containers">
+<h2>Combining Querysets / Querysets as &quot;Object Containers&quot;</h2>
+<p>Querysets could now be regarded as object containers that allow the
+aggregation of different object types, very similar to python
+lists - as long as the objects are accessed through the manager of
+a common base class:</p>
<pre class="doctest-block">
&gt;&gt;&gt; Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
.
@@ -432,75 +450,73 @@
<pre class="literal-block">
from thirdparty import ThirdPartyModel
-class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
+class MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
</pre>
<p>Or instead integrating the third party model anywhere into an
existing polymorphic inheritance tree:</p>
<pre class="literal-block">
-class MyModel(SomePolymorphicModel):
+class MyBaseModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)
-class MyModelWithThirdParty(MyModel, ThirdPartyModel):
+class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
pass # or add fields
</pre>
</div>
<div class="section" id="non-polymorphic-queries">
<h2>Non-Polymorphic Queries</h2>
<pre class="doctest-block">
-&gt;&gt;&gt; ModelA.base_objects.all()
+&gt;&gt;&gt; qs=ModelA.objects.non_polymorphic().all()
+&gt;&gt;&gt; qs
.
[ &lt;ModelA: id 1, field1 (CharField)&gt;,
&lt;ModelA: id 2, field1 (CharField)&gt;,
&lt;ModelA: id 3, field1 (CharField)&gt; ]
</pre>
-<p>Each polymorphic model has 'base_objects' defined as a normal
-Django manager. Of course, arbitrary custom managers may be
-added to the models as well.</p>
+<p>If you insert <tt class="docutils literal">.non_polymorphic()</tt> anywhere into the query chain, then
+django_polymorphic will simply leave out the final step of retrieving the
+real objects, and the manager/queryset will return objects of the type of
+the base class you used for the query, like vanilla Django would
+(<tt class="docutils literal">ModelA</tt> in this example).</p>
+<p>There are no other changes in the behaviour of the queryset. For example,
+enhancements for <tt class="docutils literal">filter()</tt> or <tt class="docutils literal">instance_of()</tt> etc. still work as expected.
+If you do the final step yourself, you get the usual polymorphic result:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; qs.get_real_instances()
+[ &lt;ModelA: id 1, field1 (CharField)&gt;,
+ &lt;ModelB: id 2, field1 (CharField), field2 (CharField)&gt;,
+ &lt;ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)&gt; ]
+</pre>
</div>
<div class="section" id="about-queryset-methods">
<h2>About Queryset Methods</h2>
<ul class="simple">
<li><tt class="docutils literal">annotate()</tt> and <tt class="docutils literal">aggregate()</tt> work just as usual, with the
addition that the <tt class="docutils literal">ModelX___field</tt> syntax can be used for the
keyword arguments (but not for the non-keyword arguments).</li>
-</ul>
-<ul class="simple">
<li><tt class="docutils literal">order_by()</tt> now similarly supports the <tt class="docutils literal">ModelX___field</tt> syntax
for specifying ordering through a field in a submodel.</li>
-</ul>
-<ul class="simple">
<li><tt class="docutils literal">distinct()</tt> works as expected. It only regards the fields of
the base class, but this should never make a difference.</li>
-</ul>
-<ul class="simple">
<li><tt class="docutils literal">select_related()</tt> works just as usual, but it can not (yet) be used
to select relations in derived models
(like <tt class="docutils literal"><span class="pre">ModelA.objects.select_related('ModelC___fieldxy')</span></tt> )</li>
-</ul>
-<ul class="simple">
-<li><tt class="docutils literal">extra()</tt> by default works exactly like the original version,
-with the resulting queryset not being polymorphic. There is
-experimental support for a polymorphic extra() via the keyword
-argument <tt class="docutils literal">polymorphic=True</tt> (only the <tt class="docutils literal">where</tt> and
-<tt class="docutils literal">order_by</tt> and <tt class="docutils literal">params</tt> arguments of extra() should be used then).
-The behaviour of extra() may change in the future, so it's best if you use
-<tt class="docutils literal"><span class="pre">base_objects=ModelA.base_objects.extra(...)</span></tt> instead if you want to
-sure to get non-polymorphic behaviour.</li>
-</ul>
-<ul class="simple">
-<li><tt class="docutils literal">get_real_instances(base_objects_list_or_queryset)</tt> allows you to turn a
+<li><tt class="docutils literal">extra()</tt> works as expected (it returns polymorphic results) but
+currently has one restriction: The resulting objects are required to have
+a unique primary key within the result set - otherwise an error is thrown
+(this case could be made to work, however it may be mostly unneeded)..
+The keyword-argument &quot;polymorphic&quot; is no longer supported.
+You can get back the old non-polymorphic behaviour (before V1.0)
+by using <tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra(...)</span></tt>.</li>
+<li><tt class="docutils literal">get_real_instances()</tt> allows you to turn a
queryset or list of base model objects efficiently into the real objects.
-For example, you could do <tt class="docutils literal"><span class="pre">base_objects=ModelA.base_objects.extra(...)</span></tt> and
-then call <tt class="docutils literal">real_objects=ModelA.objects.get_real_instances(base_objects)</tt>.</li>
-</ul>
-<ul class="simple">
+For example, you could do <tt class="docutils literal"><span class="pre">base_objects_queryset=ModelA.extra(...).non_polymorphic()</span></tt>
+and then call <tt class="docutils literal">real_objects=base_objects_queryset.get_real_instances()</tt>.Or alternatively
+.``real_objects=ModelA.objects..get_real_instances(base_objects_queryset_or_object_list)``</li>
<li><tt class="docutils literal">values()</tt> &amp; <tt class="docutils literal">values_list()</tt> currently do not return polymorphic
results. This may change in the future however. If you want to use these
methods now, it's best if you use <tt class="docutils literal"><span class="pre">Model.base_objects.values...</span></tt> as
this is guaranteed to not change.</li>
-</ul>
-<ul class="simple">
<li><tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet supported (support will be added
in the future).</li>
</ul>
@@ -537,27 +553,40 @@
</pre>
<p>You may also use ShowFieldContent or ShowFieldTypeAndContent to display
additional information when printing querysets (or converting them to text).</p>
+<p>When showing field contents, they will be truncated to 20 characters. You can
+modify this behaviour by setting a class variable like this:</p>
+<pre class="literal-block">
+class ModelA(ShowFieldType, PolymorphicModel):
+ polymorphic_showfield_max_field_width = 20
+ ...
+</pre>
</div>
</div>
<div class="section" id="custom-managers-querysets-manager-inheritance">
-<h1><a class="toc-backref" href="#id5">Custom Managers, Querysets &amp; Manager Inheritance</a></h1>
+<h1><a class="toc-backref" href="#id6">Custom Managers, Querysets &amp; Manager Inheritance</a></h1>
<div class="section" id="using-a-custom-manager">
<h2>Using a Custom Manager</h2>
<p>A nice feature of Django is the possibility to define one's own custom object managers.
This is fully supported with django_polymorphic: For creating a custom polymorphic
manager class, just derive your manager from <tt class="docutils literal">PolymorphicManager</tt> instead of
-<tt class="docutils literal">models.Manager</tt>. Just as with vanilla Django, in your model class, you should
+<tt class="docutils literal">models.Manager</tt>. As with vanilla Django, in your model class, you should
explicitly add the default manager first, and then your custom manager:</p>
<pre class="literal-block">
-from polymorphic import PolymorphicModel, PolymorphicManager
+ from polymorphic import PolymorphicModel, PolymorphicManager
-class MyOrderedManager(PolymorphicManager):
- def get_query_set(self):
- return super(MyOrderedManager,self).get_query_set().order_by('some_field')
+class TimeOrderedManager(PolymorphicManager):
+ def get_query_set(self):
+ qs = super(TimeOrderedManager,self).get_query_set()
+ return qs.order_by('-start_date') # order the queryset
-class MyModel(PolymorphicModel):
- objects = PolymorphicManager() # add the default polymorphic manager first
- ordered_objects = MyOrderedManager() # then add your own manager
+ def most_recent(self):
+ qs = self.get_query_set() # get my ordered queryset
+ return qs[:10] # limit =&gt; get ten most recent entries
+
+ class Project(PolymorphicModel):
+ objects = PolymorphicManager() # add the default polymorphic manager first
+ objects_ordered = TimeOrderedManager() # then add your own manager
+ start_date = DateTimeField() # project start is this date/time
</pre>
<p>The first manager defined ('objects' in the example) is used by
Django as automatic manager for several purposes, including accessing
@@ -568,18 +597,33 @@
<h2>Manager Inheritance</h2>
<p>Polymorphic models inherit/propagate all managers from their
base models, as long as these are polymorphic. This means that all
-managers defined in polymorphic base models work just the same as if
-they were defined in the new model.</p>
-<p>An example (inheriting from MyModel above):</p>
+managers defined in polymorphic base models continue to work as
+expected in models inheriting from this base model:</p>
<pre class="literal-block">
-class MyModel2(MyModel):
- pass
+from polymorphic import PolymorphicModel, PolymorphicManager
+
+class TimeOrderedManager(PolymorphicManager):
+ def get_query_set(self):
+ qs = super(TimeOrderedManager,self).get_query_set()
+ return qs.order_by('-start_date') # order the queryset
+
+ def most_recent(self):
+ qs = self.get_query_set() # get my ordered queryset
+ return qs[:10] # limit =&gt; get ten most recent entries
-# Managers inherited from MyModel:
-# the regular 'objects' manager and the custom 'ordered_objects' manager
-&gt;&gt;&gt; MyModel2.objects.all()
-&gt;&gt;&gt; MyModel2.ordered_objects.all()
+ class Project(PolymorphicModel):
+ objects = PolymorphicManager() # add the default polymorphic manager first
+ objects_ordered = TimeOrderedManager() # then add your own manager
+ start_date = DateTimeField() # project start is this date/time
+
+ class ArtProject(Project): # inherit from Project, inheriting its fields and managers
+ artist = models.CharField(max_length=30)
</pre>
+<p>ArtProject inherited the managers <tt class="docutils literal">objects</tt> and <tt class="docutils literal">objects_ordered</tt> from Project.</p>
+<p><tt class="docutils literal">ArtProject.objects_ordered.all()</tt> will return all art projects ordered
+regarding their start time and <tt class="docutils literal">ArtProject.objects_ordered.most_recent()</tt>
+will return the ten most recent art projects.
+.</p>
</div>
<div class="section" id="using-a-custom-queryset-class">
<h2>Using a Custom Queryset Class</h2>
@@ -601,10 +645,16 @@
</div>
</div>
<div class="section" id="performance-considerations">
-<h1><a class="toc-backref" href="#id6">Performance Considerations</a></h1>
-<p>The current implementation is pretty simple and does not use any
+<h1><a class="toc-backref" href="#id7">Performance Considerations</a></h1>
+<p>The current implementation is rather simple and does not use any
custom SQL or Django DB layer internals - it is purely based on the
-standard Django ORM. Right now the query</p>
+standard Django ORM.</p>
+<p>The advantages are that the implementation naturally works on all
+supported database management systems, and consists of rather
+clean source code which can be easily understood and enhanced.</p>
+<p>The disadvantage is that this approach can not deliver the optimum
+performance as it introduces additional database queries.</p>
+<p>Specifically, the query:</p>
<pre class="literal-block">
result_objects = list( ModelA.objects.filter(...) )
</pre>
@@ -615,92 +665,102 @@
two queries are executed. The pathological worst case is 101 db queries if
result_objects contains 100 different object types (with all of them
subclasses of <tt class="docutils literal">ModelA</tt>).</p>
-<p>Performance ist relative: when Django users create their own
-polymorphic ad-hoc solution (without a tool like <tt class="docutils literal">django_polymorphic</tt>),
-this usually results in a variation of</p>
+<p>Usually, when Django users create their own polymorphic ad-hoc solution
+without a tool like django_polymorphic, this usually results in a variation of</p>
<pre class="literal-block">
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
</pre>
-<p>which has really bad performance. Relative to this, the
-performance of the current <tt class="docutils literal">django_polymorphic</tt> is pretty good.
-It's probably efficient enough for the majority of use cases.</p>
-<p>Chunking: The implementation always requests objects in chunks of
-size <tt class="docutils literal">Polymorphic_QuerySet_objects_per_request</tt>. This limits the
-complexity/duration for each query, including the pathological cases.</p>
+<p>which has exceptionally bad performance, as it introduces one additional
+SQL query for every object in the result which is not of class <tt class="docutils literal">BaseModel</tt>.
+Relative to this, the performance of the current django_polymorphic
+implementation is very good.</p>
+<p>If your project however needs perfect performance and the current
+performance implications of django_polymorphic are not acceptable, then
+basically there are the two options of either foregoing of an essential aspect
+of object oriented programming or optimizing django_polymorphic.</p>
+<p>Foregoing the benefits of this aspect of object oriented programming
+for projects that could benefit from it will however usually lead to bloated code,
+unnecessary complexity and considerably more of the programmer's time to
+create and update the implementation, together with the disadvantages
+of a less flexible and less future-proof solution. Throwing a little more
+hardware on the problem might be the least expensive solution in most cases.</p>
</div>
<div class="section" id="possible-optimizations">
-<h1><a class="toc-backref" href="#id7">Possible Optimizations</a></h1>
-<p><tt class="docutils literal">PolymorphicQuerySet</tt> can be optimized to require only one SQL query
-for the queryset evaluation and retrieval of all objects.</p>
-<p>Basically, what ist needed is a possibility to pull in the fields
-from all relevant sub-models with one SQL query. However, some deeper
-digging into the Django database layer will be required in order to
-make this happen.</p>
-<p>An optimized version might require an SQL database. For non-SQL databases
-the implementation could fall back to the current ORM-only
-implementation.</p>
+<h1><a class="toc-backref" href="#id8">Possible Optimizations</a></h1>
+<p>Django_polymorphic can be optimized to require only one
+SQL query for the queryset evaluation and retrieval of all objects.</p>
+<p>Probably all that would be needed seems support for an additional
+queryset function in Django's database layer, like:</p>
+<pre class="literal-block">
+ModelA.objects.join_models(on=&quot;field_name&quot;, models=[ModelB, ModelC])
+</pre>
+<p>or, less general but more simple:</p>
+<pre class="literal-block">
+ModelA.objects.join_tables(on=&quot;field_name&quot;, tables=['tableB','tableC'])
+</pre>
+<p>This would add additional left outer joins to the query and then add
+the resulting fields from this join to the result objects.
+E.g. a query for <tt class="docutils literal">ModelA</tt> objects would need to join the <tt class="docutils literal">ModelB</tt>
+and <tt class="docutils literal">ModelC</tt> tables on the the field <tt class="docutils literal">id</tt> and add the fields <tt class="docutils literal">field2</tt>
+and <tt class="docutils literal">field3</tt> from the joined tables to the resulting objects.</p>
+<p>An optimization like this might require an SQL database.
+For non-SQL databases the implementation could fall back to
+the current ORM-only implementation.</p>
<div class="section" id="sql-complexity-of-an-optimized-implementation">
<h2>SQL Complexity of an Optimized Implementation</h2>
<p>With only one SQL query, one SQL join for each possible subclass
-would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).
-With two SQL queries, the number of joins could be reduced to the
-number of actuallly occurring subclasses in the result. A final
-implementation might want to use one query only if the number of
-possible subclasses (and therefore joins) is not too large, and
-two queries otherwise (using the first query to determine the
-actually occurring subclasses, reducing the number of joins for
-the second).</p>
+would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).</p>
+<p>With two SQL queries, the number of joins could be reduced to the
+number of actuallly occurring subclasses in the specific result.</p>
+<p>A perfect implementation might want to use one query only
+if the number of possible subclasses (and therefore joins) is not
+too large, and two queries otherwise (using the first query to
+determine the actually occurring subclasses, reducing the number
+of joins for the second).</p>
<p>The number of joins needed for polymorphic object retrieval might
raise concerns regarding the efficiency of these database
queries. It seems likely however, that the increased number of joins
is no problem for the supported DBM systems in all realistic use cases.
Should the number of joins of the more extreme use cases turn out to
be problematic, it is possible to split any problematic query into, for example,
two queries with only half the number of joins each.</p>
-</div>
-<div class="section" id="in-general">
-<h2>In General</h2>
-<p>Let's not forget that the above is just about optimization.
-The current implementation already works well - and perhaps well
-enough for the majority of applications.</p>
-<p>Also, it seems that further optimization (down to one DB request)
-would be restricted to a relatively small area of the code, and
-be mostly independent from the rest of the module.
-So it seems this optimization can be done at any later time
-(like when it's needed).</p>
+<p>It seems that further optimization (down to one DB request)
+of django_polymorphic would be restricted to a relatively small area of
+the code (&quot;query.py&quot;), and be pretty much independent from the rest of the module.
+Such an optimization can be done at any later time (like when it's needed).</p>
</div>
</div>
<div class="section" id="restrictions-caveats">
-<span id="restrictions"></span><h1><a class="toc-backref" href="#id8">Restrictions &amp; Caveats</a></h1>
-<ul class="simple">
-<li>The queryset methods <tt class="docutils literal">values()</tt>, <tt class="docutils literal">values_list()</tt>, <tt class="docutils literal">select_related()</tt>,
-<tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet fully supported (see above)</li>
-</ul>
+<span id="restrictions"></span><h1><a class="toc-backref" href="#id9">Restrictions &amp; Caveats</a></h1>
<ul class="simple">
+<li>Queryset methods <tt class="docutils literal">values()</tt>, <tt class="docutils literal">values_list()</tt>, <tt class="docutils literal">select_related()</tt>,
+<tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet fully supported (see above).
+<tt class="docutils literal">extra()</tt> has one restriction: the resulting objects are required to have
+a unique primary key within the result set</li>
<li>Django Admin Integration: There currently is no specific admin integration,
but it would most likely make sense to have one.</li>
-</ul>
-<ul class="simple">
<li>Diamond shaped inheritance: There seems to be a general problem
with diamond shaped multiple model inheritance with Django models
(tested with V1.1 - V1.3).
An example is here: <a class="reference external" href="http://code.djangoproject.com/ticket/10808">http://code.djangoproject.com/ticket/10808</a>.
This problem is aggravated when trying to enhance models.Model
by subclassing it instead of modifying Django core (as we do here
with PolymorphicModel).</li>
-</ul>
-<ul class="simple">
<li>The enhanced filter-definitions/Q-objects only work as arguments
for the methods of the polymorphic querysets. Please see above
for <tt class="docutils literal">translate_polymorphic_Q_object</tt>.</li>
-</ul>
-<ul class="simple">
+<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
+in the base model (the base model directly inheriting from
+PolymorphicModel). You need to be aware of this when using the
+<tt class="docutils literal">dumpdata</tt> management command or any other low-level
+database operations. E.g. if you rename models or apps or copy
+objects from one database to another, then Django's ContentType
+table needs to be corrected/copied too. This is of course generally
+the case for any models using Django's ContentType.</li>
<li>Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 &quot;related_name&quot;
option (fixed in Django 1.2).</li>
-</ul>
-<ul class="simple">
<li>Django 1.1 only - when ContentType is used in models, Django's
seralisation or fixtures cannot be used (all polymorphic models
use ContentType). This issue seems to be resolved for Django 1.2
@@ -710,25 +770,15 @@
</ul>
</li>
</ul>
-<ul class="simple">
-<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
-in the base model (the base model directly inheriting from
-PolymorphicModel). You need to be aware of this when using the
-<tt class="docutils literal">dumpdata</tt> management command or any other low-level
-database operations. E.g. if you rename models or apps or copy
-objects from one database to another, then Django's ContentType
-table needs to be corrected/copied too. This is of course generally
-the case for any models using Django's ContentType.</li>
-</ul>
</div>
<div class="section" id="project-status">
-<h1><a class="toc-backref" href="#id9">Project Status</a></h1>
+<h1><a class="toc-backref" href="#id10">Project Status</a></h1>
<p>Django_polymorphic works well for a considerable number of users now,
and no major problems have shown up for many months.
-The API can be considered stable beginning with this release.</p>
+The API can be considered stable beginning with the V1.0 release.</p>
</div>
<div class="section" id="links">
-<h1><a class="toc-backref" href="#id10">Links</a></h1>
+<h1><a class="toc-backref" href="#id11">Links</a></h1>
<ul class="simple">
<li><a class="reference external" href="http://code.djangoproject.com/wiki/ModelInheritance">http://code.djangoproject.com/wiki/ModelInheritance</a></li>
<li><a class="reference external" href="http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html">http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html</a></li>
View
313 DOCS.rst
@@ -11,7 +11,7 @@ Quickstart
Install
-------
-After uncompressing (if necessary), in the directory "django_polymorphic",
+After uncompressing (if necessary), in the directory "...django_polymorphic",
execute (on Unix-like systems)::
sudo python setup.py install
@@ -37,47 +37,61 @@ All models inheriting from your polymorphic models will be polymorphic as well.
Create some objects
-------------------
->>> Project.objects.create(topic="John's Gathering")
->>> ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
+>>> Project.objects.create(topic="Office Meeting")
+>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
Get polymorphic query results
-----------------------------
>>> Project.objects.all()
-[ <Project: id 1, topic: "John's Gathering">,
- <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
- <ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
+[ <Project: id 1, topic "John's Gathering">,
+ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
+ <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
-using instance_of and not_instance_of for narrowing the result to specific subtypes:
+use ``instance_of`` or ``not_instance_of`` for narrowing the result to specific subtypes:
>>> Project.objects.instance_of(ArtProject)
-[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner"> ]
+[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner"> ]
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
-[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
- <ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
+[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
+ <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
-Polymorphic filtering: Let's get all projects where Mr. Turner is involved as an artist
+Polymorphic filtering: Get all projects where Mr. Turner is involved as an artist
or supervisor (note the three underscores):
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
-[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
- <ResearchProject: id 3, topic: "History of Sculpting", supervisor: "T. Turner"> ]
+[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
+ <ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
-What's More?
--------------
-
-Most of Django's standard ORM functionality is available and works as expected.
-ForeignKeys, ManyToManyFields and OneToToneFields to your polymorphic
-models work as shey should (polymorphic).
-
-In short, with django_polymorphic the Django models are much more "pythonic", i.e.
-they just work as you expect them to work: very similar to ordinary python classes
-(which is not the case with vanilla Django model inheritance).
+This is basically all you need to know, as django_polymorphic mostly
+works fully automatic and just delivers the expected ("pythonic") results.
Note: In all example output, above and below, for a nicer and more informative
-output the `ShowFieldType` mixin has been used (documented below).
+output the ``ShowFieldType`` mixin has been used (documented below).
+
+
+List of Features
+================
+
+* Fully automatic - generally makes sure that the same objects are
+ returned from the database that were stored there, regardless how
+ they are retrieved
+* Only on models that request polymorphic behaviour however (and the
+ models inheriting from them)
+* Full support for ForeignKeys, ManyToManyFields and OneToToneFields
+* Filtering for classes, equivalent to python's isinstance():
+ ``instance_of(...)`` and ``not_instance_of(...)``
+* Polymorphic filtering/ordering etc., allowing the use of fields of
+ derived models ("ArtProject___artist")
+* Support for user-defined custom managers
+* Automatic inheritance of custom managers
+* Support for user-defined custom queryset classes
+* Non-polymorphic queries if needed, with no other change in
+ features/behaviour
+* Combining querysets of different types/models ("qs3 = qs1 | qs2")
+* Nice/informative display of polymorphic queryset results
More about Installation / Testing
@@ -86,40 +100,39 @@ More about Installation / Testing
Requirements
------------
-Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
-on Django 1.1.1 / 1.2 beta and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.
+Django 1.1 (or later) and Python 2.4 or later. This code has been tested
+on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.
Included Test Suite
-------------------
The repository (or tar file) contains a complete Django project
that may be used for tests or experiments, without any installation needed.
-To run the included test suite, execute::
+To run the included test suite, in the directory "...django_polymorphic" execute::
./manage test polymorphic
-The management command ``pcmd.py`` in the app ``pexp`` can be used for
-experiments - modify this file (pexp/management/commands/pcmd.py)
+The management command ``pcmd.py`` in the app ``pexp`` can be used
+for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
to your liking, then run::
./manage syncdb # db is created in /var/tmp/... (settings.py)
./manage pcmd
-
+
Installation
------------
-In the directory "django_polymorphic", execute ``sudo python setup.py install``.
+In the directory "...django_polymorphic", execute ``sudo python setup.py install``.
-Alternatively you can simply copy the ``polymorphic`` directory
+Alternatively you can simply copy the ``polymorphic`` subdirectory
(under "django_polymorphic") into your Django project dir
(e.g. if you want to distribute your project with more 'batteries included').
-If you want to use the management command ``polymorphic_dumpdata``, then
-you need to add ``polymorphic`` to your INSTALLED_APPS setting. This is also
-needed if you want to run the test cases in `polymorphic/tests.py`.
+If you want to run the test cases in `polymorphic/tests.py`, you need to add
+``polymorphic`` to your INSTALLED_APPS setting.
-In any case, Django's ContentType framework (``django.contrib.contenttypes``)
+Django's ContentType framework (``django.contrib.contenttypes``)
needs to be listed in INSTALLED_APPS (usually it already is).
@@ -139,6 +152,7 @@ In the examples below, these models are being used::
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
+
Filtering for classes (equivalent to python's isinstance() ):
-------------------------------------------------------------
@@ -156,6 +170,7 @@ You can also use this feature in Q-objects (with the same result as above):
>>> ModelA.objects.filter( Q(instance_of=ModelB) )
+
Polymorphic filtering (for fields in derived classes)
-----------------------------------------------------
@@ -168,19 +183,21 @@ syntax: ``exact model name + three _ + field name``):
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
-Combining Querysets of different types/models
----------------------------------------------
-Querysets may now be regarded as object containers that allow the
-aggregation of different object types - very similar to python
-lists (as long as the objects are accessed through the manager of
-a common base class):
+Combining Querysets / Querysets as "Object Containers"
+------------------------------------------------------
+
+Querysets could now be regarded as object containers that allow the
+aggregation of different object types, very similar to python
+lists - as long as the objects are accessed through the manager of
+a common base class:
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
.
[ <ModelX: id 1, field_x (CharField)>,
<ModelY: id 2, field_y (CharField)> ]
+
ManyToManyField, ForeignKey, OneToOneField
------------------------------------------
@@ -211,6 +228,7 @@ A ManyToManyField example::
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
+
Using Third Party Models (without modifying them)
-------------------------------------------------
@@ -220,18 +238,19 @@ model as the root of a polymorphic inheritance tree::
from thirdparty import ThirdPartyModel
- class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
+ class MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
Or instead integrating the third party model anywhere into an
existing polymorphic inheritance tree::
- class MyModel(SomePolymorphicModel):
+ class MyBaseModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)
- class MyModelWithThirdParty(MyModel, ThirdPartyModel):
+ class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
pass # or add fields
+
Non-Polymorphic Queries
-----------------------
@@ -241,11 +260,17 @@ real objects, and the manager/queryset will return objects of the type of
the base class you used for the query, like vanilla Django would
(``ModelA`` in this example).
+>>> qs=ModelA.objects.non_polymorphic().all()
+>>> qs
+[ <ModelA: id 1, field1 (CharField)>,
+ <ModelA: id 2, field1 (CharField)>,
+ <ModelA: id 3, field1 (CharField)> ]
+
There are no other changes in the behaviour of the queryset. For example,
enhancements for ``filter()`` or ``instance_of()`` etc. still work as expected.
If you do the final step yourself, you get the usual polymorphic result:
->>> qs.get_real_instances()
+>>> ModelA.objects.get_real_instances(qs)
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
@@ -258,23 +283,25 @@ About Queryset Methods
addition that the ``ModelX___field`` syntax can be used for the
keyword arguments (but not for the non-keyword arguments).
-+ ``order_by()`` now similarly supports the ``ModelX___field`` syntax
+* ``order_by()`` now similarly supports the ``ModelX___field`` syntax
for specifying ordering through a field in a submodel.
* ``distinct()`` works as expected. It only regards the fields of
the base class, but this should never make a difference.
-+ ``select_related()`` works just as usual, but it can not (yet) be used
+* ``select_related()`` works just as usual, but it can not (yet) be used
to select relations in derived models
(like ``ModelA.objects.select_related('ModelC___fieldxy')`` )
-* ``extra()`` works as expected (returns polymorphic results) but
+* ``extra()`` works as expected (it returns polymorphic results) but
currently has one restriction: The resulting objects are required to have
a unique primary key within the result set - otherwise an error is thrown
(this case could be made to work, however it may be mostly unneeded)..
The keyword-argument "polymorphic" is no longer supported.
+ You can get back the old non-polymorphic behaviour (before V1.0)
+ by using ``ModelA.objects.non_polymorphic().extra(...)``.
-+ ``get_real_instances()`` allows you to turn a
+* ``get_real_instances()`` allows you to turn a
queryset or list of base model objects efficiently into the real objects.
For example, you could do ``base_objects_queryset=ModelA.extra(...).non_polymorphic()``
and then call ``real_objects=base_objects_queryset.get_real_instances()``.Or alternatively
@@ -285,9 +312,10 @@ About Queryset Methods
methods now, it's best if you use ``Model.base_objects.values...`` as
this is guaranteed to not change.
-+ ``defer()`` and ``only()`` are not yet supported (support will be added
+* ``defer()`` and ``only()`` are not yet supported (support will be added
in the future).
+
Using enhanced Q-objects in any Places
--------------------------------------
@@ -308,6 +336,7 @@ vanilla django function ``ForeignKey`` cannot process. In such cases you can do
somekey = model.ForeignKey(Model2A,
limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) )
+
Nicely Displaying Polymorphic Querysets
---------------------------------------
@@ -322,6 +351,15 @@ ShowFieldType class mixin::
You may also use ShowFieldContent or ShowFieldTypeAndContent to display
additional information when printing querysets (or converting them to text).
+When showing field contents, they will be truncated to 20 characters. You can
+modify this behaviour by setting a class variable in your model like this::
+
+ class ModelA(ShowFieldType, PolymorphicModel):
+ polymorphic_showfield_max_field_width = 20
+ ...
+
+Similarly, pre-V1.0 output formatting can be re-estated by using
+``polymorphic_showfield_old_format = True``.
Custom Managers, Querysets & Manager Inheritance
================================================
@@ -332,18 +370,24 @@ Using a Custom Manager
A nice feature of Django is the possibility to define one's own custom object managers.
This is fully supported with django_polymorphic: For creating a custom polymorphic
manager class, just derive your manager from ``PolymorphicManager`` instead of
-``models.Manager``. Just as with vanilla Django, in your model class, you should
+``models.Manager``. As with vanilla Django, in your model class, you should
explicitly add the default manager first, and then your custom manager::
- from polymorphic import PolymorphicModel, PolymorphicManager
+ from polymorphic import PolymorphicModel, PolymorphicManager
- class MyOrderedManager(PolymorphicManager):
- def get_query_set(self):
- return super(MyOrderedManager,self).get_query_set().order_by('some_field')
-
- class MyModel(PolymorphicModel):
- objects = PolymorphicManager() # add the default polymorphic manager first
- ordered_objects = MyOrderedManager() # then add your own manager
+ class TimeOrderedManager(PolymorphicManager):
+ def get_query_set(self):
+ qs = super(TimeOrderedManager,self).get_query_set()
+ return qs.order_by('-start_date') # order the queryset
+
+ def most_recent(self):
+ qs = self.get_query_set() # get my ordered queryset
+ return qs[:10] # limit => get ten most recent entries
+
+ class Project(PolymorphicModel):
+ objects = PolymorphicManager() # add the default polymorphic manager first
+ objects_ordered = TimeOrderedManager() # then add your own manager
+ start_date = DateTimeField() # project start is this date/time
The first manager defined ('objects' in the example) is used by
Django as automatic manager for several purposes, including accessing
@@ -355,18 +399,34 @@ Manager Inheritance
Polymorphic models inherit/propagate all managers from their
base models, as long as these are polymorphic. This means that all
-managers defined in polymorphic base models work just the same as if
-they were defined in the new model.
+managers defined in polymorphic base models continue to work as
+expected in models inheriting from this base model::
-An example (inheriting from MyModel above)::
+ from polymorphic import PolymorphicModel, PolymorphicManager
- class MyModel2(MyModel):
- pass
+ class TimeOrderedManager(PolymorphicManager):
+ def get_query_set(self):
+ qs = super(TimeOrderedManager,self).get_query_set()
+ return qs.order_by('-start_date') # order the queryset
- # Managers inherited from MyModel:
- # the regular 'objects' manager and the custom 'ordered_objects' manager
- >>> MyModel2.objects.all()
- >>> MyModel2.ordered_objects.all()
+ def most_recent(self):
+ qs = self.get_query_set() # get my ordered queryset
+ return qs[:10] # limit => get ten most recent entries
+
+ class Project(PolymorphicModel):
+ objects = PolymorphicManager() # add the default polymorphic manager first
+ objects_ordered = TimeOrderedManager() # then add your own manager
+ start_date = DateTimeField() # project start is this date/time
+
+ class ArtProject(Project): # inherit from Project, inheriting its fields and managers
+ artist = models.CharField(max_length=30)
+
+ArtProject inherited the managers ``objects`` and ``objects_ordered`` from Project.
+
+``ArtProject.objects_ordered.all()`` will return all art projects ordered
+regarding their start time and ``ArtProject.objects_ordered.most_recent()``
+will return the ten most recent art projects.
+.
Using a Custom Queryset Class
-----------------------------
@@ -385,14 +445,23 @@ instead of Django's QuerySet as the base class::
class MyModel(PolymorphicModel):
my_objects=PolymorphicManager(MyQuerySet)
...
-
+
Performance Considerations
==========================
-The current implementation is pretty simple and does not use any
+The current implementation is rather simple and does not use any
custom SQL or Django DB layer internals - it is purely based on the
-standard Django ORM. Right now the query ::
+standard Django ORM.
+
+The advantages are that the implementation naturally works on all
+supported database management systems, and consists of rather
+clean source code which can be easily understood and enhanced.
+
+The disadvantage is that this approach can not deliver the optimum
+performance as it introduces additional database queries.
+
+Specifically, the query::
result_objects = list( ModelA.objects.filter(...) )
@@ -404,48 +473,68 @@ two queries are executed. The pathological worst case is 101 db queries if
result_objects contains 100 different object types (with all of them
subclasses of ``ModelA``).
-Performance ist relative: when Django users create their own
-polymorphic ad-hoc solution (without a tool like ``django_polymorphic``),
-this usually results in a variation of ::
+Usually, when Django users create their own polymorphic ad-hoc solution
+without a tool like django_polymorphic, this usually results in a variation of ::
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
-which has really bad performance. Relative to this, the
-performance of the current ``django_polymorphic`` is pretty good.
-It's probably efficient enough for the majority of use cases.
+which has exceptionally bad performance, as it introduces one additional
+SQL query for every object in the result which is not of class ``BaseModel``.
+Relative to this, the performance of the current django_polymorphic
+implementation is very good.
+
+If your project however needs perfect performance and the current
+performance implications of django_polymorphic are not acceptable, then
+basically there are the two options of either foregoing of an essential aspect
+of object oriented programming or optimizing django_polymorphic.
-Chunking: The implementation always requests objects in chunks of
-size ``Polymorphic_QuerySet_objects_per_request``. This limits the
-complexity/duration for each query, including the pathological cases.
+Foregoing the benefits of this aspect of object oriented programming
+for projects that could benefit from it will however usually lead to bloated code,
+unnecessary complexity and considerably more of the programmer's time to
+create and update the implementation, together with the disadvantages
+of a less flexible and less future-proof solution. Throwing a little more
+hardware on the problem might be the least expensive solution in most cases.
Possible Optimizations
======================
-``PolymorphicQuerySet`` can be optimized to require only one SQL query
-for the queryset evaluation and retrieval of all objects.
+Django_polymorphic can be optimized to require only one
+SQL query for the queryset evaluation and retrieval of all objects.
-Basically, what ist needed is a possibility to pull in the fields
-from all relevant sub-models with one SQL query. However, some deeper
-digging into the Django database layer will be required in order to
-make this happen.
+Probably all that would be needed seems support for an additional
+queryset function in Django's database layer, like::
-An optimized version might require an SQL database. For non-SQL databases
-the implementation could fall back to the current ORM-only
-implementation.
+ ModelA.objects.join_models(on="field_name", models=[ModelB, ModelC])
+
+or, less general but more simple::
+
+ ModelA.objects.join_tables(on="field_name", tables=['myapp_modelb','myapp_modelc'])
+
+This would add additional left outer joins to the query and then add
+the resulting fields from this join to the result objects.
+E.g. a query for ``ModelA`` objects would need to join the ``ModelB``
+and ``ModelC`` tables on the the field ``id`` and add the fields ``field2``
+and ``field3`` from the joined tables to the resulting objects.
+
+An optimization like this might require an SQL database.
+For non-SQL databases the implementation could fall back to
+the current ORM-only implementation.
SQL Complexity of an Optimized Implementation
---------------------------------------------
With only one SQL query, one SQL join for each possible subclass
would be needed (``BaseModel.__subclasses__()``, recursively).
+
With two SQL queries, the number of joins could be reduced to the
-number of actuallly occurring subclasses in the result. A final
-implementation might want to use one query only if the number of
-possible subclasses (and therefore joins) is not too large, and
-two queries otherwise (using the first query to determine the
-actually occurring subclasses, reducing the number of joins for
-the second).
+number of actuallly occurring subclasses in the specific result.
+
+A perfect implementation might want to use one query only
+if the number of possible subclasses (and therefore joins) is not
+too large, and two queries otherwise (using the first query to
+determine the actually occurring subclasses, reducing the number
+of joins for the second).
The number of joins needed for polymorphic object retrieval might
raise concerns regarding the efficiency of these database
@@ -455,29 +544,23 @@ Should the number of joins of the more extreme use cases turn out to
be problematic, it is possible to split any problematic query into, for example,
two queries with only half the number of joins each.
-In General
-----------
-
-Let's not forget that the above is just about optimization.
-The current implementation already works well - and perhaps well
-enough for the majority of applications.
-
-Also, it seems that further optimization (down to one DB request)
-would be restricted to a relatively small area of the code, and
-be mostly independent from the rest of the module.
-So it seems this optimization can be done at any later time
-(like when it's needed).
+It seems that further optimization (down to one DB request)
+of django_polymorphic would be restricted to a relatively small area of
+the code ("query.py"), and be pretty much independent from the rest of the module.
+Such an optimization can be done at any later time (like when it's needed).
.. _restrictions:
Restrictions & Caveats
======================
-* The queryset methods ``values()``, ``values_list()``, ``select_related()``,
- ``defer()`` and ``only()`` are not yet fully supported (see above)
+* Queryset methods ``values()``, ``values_list()``, ``select_related()``,
+ ``defer()`` and ``only()`` are not yet fully supported (see above).
+ ``extra()`` has one restriction: the resulting objects are required to have
+ a unique primary key within the result set
-+ Django Admin Integration: There currently is no specific admin integration,
+* Django Admin Integration: There currently is no specific admin integration,
but it would most likely make sense to have one.
* Diamond shaped inheritance: There seems to be a general problem
@@ -488,7 +571,7 @@ Restrictions & Caveats
by subclassing it instead of modifying Django core (as we do here
with PolymorphicModel).
-+ The enhanced filter-definitions/Q-objects only work as arguments
+* The enhanced filter-definitions/Q-objects only work as arguments
for the methods of the polymorphic querysets. Please see above
for ``translate_polymorphic_Q_object``.
@@ -501,7 +584,7 @@ Restrictions & Caveats
table needs to be corrected/copied too. This is of course generally
the case for any models using Django's ContentType.
-+ Django 1.1 only - the names of polymorphic models must be unique
+* Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 "related_name"
option (fixed in Django 1.2).
@@ -515,14 +598,12 @@ Restrictions & Caveats
+ http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django
-
-
Project Status
==============
Django_polymorphic works well for a considerable number of users now,
and no major problems have shown up for many months.
-The API can be considered stable beginning with this release.
+The API can be considered stable beginning with the V1.0 release.
Links
View
100 README.html
@@ -7,13 +7,22 @@
<title></title>
<style type="text/css">
-h1, h2, h3, h4 {
+h1, h2, h3, h4,
+#table-of-contents
+{
color: #47c;
}
+h1 { padding-top: 15px; }
+h2 { padding-top: 10px; }
+h3 { padding-top: 7px; }
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
+li {
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
tt {
color: #080;
@@ -245,65 +254,61 @@
</pre>
<p>Using django_polymorphic, we simply get what we stored:</p>
<pre class="literal-block">
-[ &lt;Project: id 1, topic: &quot;John's Gathering&quot;&gt;,
- &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
- &lt;ResearchProject: id 3, topic: &quot;Swallow Aerodynamics&quot;, supervisor: &quot;Dr. Winter&quot;&gt; ]
+[ &lt;Project: id 1, topic &quot;John's Gathering&quot;&gt;,
+ &lt;ArtProject: id 2, topic &quot;Painting with Tim&quot;, artist &quot;T. Turner&quot;&gt;,
+ &lt;ResearchProject: id 3, topic &quot;Swallow Aerodynamics&quot;, supervisor &quot;Dr. Winter&quot;&gt; ]
</pre>
<p>Using vanilla Django, we get incomplete objects, which is probably not what we wanted:</p>
<pre class="literal-block">
-[ &lt;Project: id 1, topic: &quot;John's Gathering&quot;&gt;,
- &lt;Project: id 2, topic: &quot;Sculpting with Tim&quot;&gt;,
- &lt;Project: id 3, topic: &quot;Swallow Aerodynamics&quot;&gt; ]
+[ &lt;Project: id 1, topic &quot;John's Gathering&quot;&gt;,
+ &lt;Project: id 2, topic &quot;Painting with Tim&quot;&gt;,
+ &lt;Project: id 3, topic &quot;Swallow Aerodynamics&quot;&gt; ]
</pre>
<p>It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.</p>
<p>In general, the effect of django_polymorphic is twofold:</p>
<p>On one hand it makes sure that model inheritance just works
as you expect, by simply ensuring that you always get back exactly the same
objects from the database you stored there - regardless how you access them.
This can save you a lot of unpleasant workarounds.</p>
-<p>On the other hand, together with only few small API additions to the Django ORM,
+<p>On the other hand, together with a few small API additions to the Django ORM,
django_polymorphic enables a much more expressive and intuitive
programming style and also very advanced object oriented
designs that are not possible with vanilla Django.</p>
<p>Fortunately, most of the heavy duty machinery that is needed for this
functionality is already present in the original Django database layer.
-Django_polymorphic merely adds a rather thin layer above that, which is
-all that is required to make real OO fully automatic and very easy to use,
-with only minimal additions to Django's API.</p>
+Django_polymorphic adds a rather small layer above that, which is
+all that is required to make real OO fully automatic and very easy to use.</p>
<p>For more information, please look at <a class="reference external" href="http://bserve.webhop.org/django_polymorphic/DOCS.html#quickstart">Quickstart</a> or the complete
<a class="reference external" href="http://bserve.webhop.org/django_polymorphic/DOCS.html">Installation and Usage Docs</a>. Please also see the <a class="reference external" href="http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions">restrictions and caveats</a>.</p>
</div>
<div class="section" id="this-is-a-v1-0-beta-testing-release">
<h2>This is a V1.0 Beta/Testing Release</h2>
-<p>This release is mostly a cleanup and maintenance release that also
-improves a number of minor things and fixes one (non-critical) bug.</p>
-<p>Some pending API changes and corrections have been folded into this release
-in order to make the upcoming V1.0 API as stable as possible.</p>
-<p>This release is also about getting feedback from you in case you don't
-approve of any of these changes or would like to get additional
-API fixes into V1.0.</p>
<p>The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
potential problems to turn up (if they exist).</p>
-<p>If you encounter any problems please post them in the <a class="reference external" href="http://groups.google.de/group/django-polymorphic/topics">discussion group</a>
+<p>If you encounter any problems or have suggestions regarding the API or the
+changes in this beta, please post them in the <a class="reference external" href="http://groups.google.de/group/django-polymorphic/topics">discussion group</a>
or open an issue on <a class="reference external" href="http://github.com/bconstantin/django_polymorphic">GitHub</a> or <a class="reference external" href="http://bitbucket.org/bconstantin/django_polymorphic">BitBucket</a> (or send me an email).</p>
</div>
</div>
<div class="section" id="license">
<h1>License</h1>
<p>Django_polymorphic uses the same license as Django (BSD-like).</p>
</div>
-<div class="section" id="api-changes">
-<h1>API Changes</h1>
-<div class="section" id="october-18-2010">
-<h2>October 18 2010</h2>
-<div class="section" id="polymorphic-dumpdata">
-<h3>polymorphic_dumpdata</h3>
-<p>The polymorphic_dumpdata management command is not needed anymore
-and has been removed, as the regular Django dumpdata command now automatically
-works correctly with polymorphic models (for all supported versions of Django).</p>
+<div class="section" id="api-changes-additions">
+<h1>API Changes &amp; Additions</h1>
+<div class="section" id="october-26-2010-v1-0-api-changes">
+<h2>October 26 2010, V1.0 API Changes</h2>
+<div class="section" id="extra-queryset-method">
+<h3>extra() queryset method</h3>
+<p><tt class="docutils literal">.extra()</tt> has been re-implemented. Now it's polymorphic by
+default and works (nearly) without restrictions (please see docs). This is an
+incompatible API change regarding previous versions of django_polymorphic.
+Support for the <tt class="docutils literal">polymorphic</tt> keyword parameter has been removed.
+You can get back the non-polymorphic behaviour by using
+<tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra()</span></tt>.</p>
</div>
<div class="section" id="output-of-queryset-or-object-printing">
<h3>Output of Queryset or Object Printing</h3>
@@ -325,12 +330,45 @@
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
<p>(the old ones still exist for compatibility)</p>
</div>
+<div class="section" id="polymorphic-dumpdata">
+<h3>polymorphic_dumpdata</h3>
+<p>The <tt class="docutils literal">polymorphic_dumpdata</tt> management command is not needed anymore
+and has been disabled, as the regular Django dumpdata command now automatically
+works correctly with polymorphic models (for all supported versions of Django).</p>
+</div>
<div class="section" id="running-the-test-suite-with-django-1-3">
<h3>Running the Test suite with Django 1.3</h3>
<p>Django 1.3 requires <tt class="docutils literal">python manage.py test polymorphic</tt> instead of
just <tt class="docutils literal">python manage.py test</tt>.</p>
</div>
</div>
+<div class="section" id="october-26-2010-v1-0-api-additions">
+<h2>October 26 2010, V1.0 API Additions</h2>
+<ul>
+<li><p class="first"><tt class="docutils literal">.non_polymorphic()</tt> queryset member function added. This is preferable to
+using <tt class="docutils literal"><span class="pre">.base_objects...</span></tt>, as it just makes the resulting queryset non-polymorphic
+and does not change anything else in the behaviour of the manager used (while
+<tt class="docutils literal">.base_objects</tt> is just a different manager).</p>
+</li>
+<li><p class="first"><tt class="docutils literal">.get_real_instances()</tt> has been elevated to an official part of the API.
+It allows you to turn a queryset or list of base objects into a list of the real instances.
+This is useful if e.g. you use <tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra(...)</span></tt> and then want to
+transform the result to its polymorphic equivalent:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; qs = ModelA.objects.all().non_polymorphic()
+&gt;&gt;&gt; real_objects = qs.get_real_instances()
+</pre>
+<p>is equivalent to:</p>
+<pre class="doctest-block">
+&gt;&gt;&gt; real_objects = ModelA.objects.all()
+</pre>
+<p>Instead of <tt class="docutils literal">qs.get_real_instances()</tt>, <tt class="docutils literal">ModelA.objects.get_real_instances(qs)</tt> may be used
+as well. In the latter case, <tt class="docutils literal">qs</tt> may be any list of objects of type ModelA.</p>
+</li>
+<li><p class="first"><tt class="docutils literal">translate_polymorphic_Q_object</tt> (see DOCS)</p>
+</li>
+</ul>
+</div>
<div class="section" id="february-22-2010-installation-note">
<h2>February 22 2010, Installation Note</h2>
<p>The django_polymorphic source code has been restructured
@@ -354,10 +392,8 @@
</div>
<div class="section" id="january-26-2010-database-schema-change">
<h2>January 26 2010: Database Schema Change</h2>
-<div class="line-block">
-<div class="line">The update from January 26 changed the database schema (more info in the <a class="reference external" href="http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7">commit-log</a>).</div>
-<div class="line">Sorry for any inconvenience. But this should be the final DB schema now.</div>
-</div>
+<p>The update from January 26 changed the database schema (more info in the <a class="reference external" href="http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7">commit-log</a>).
+Sorry for any inconvenience. But this should be the final DB schema now.</p>
</div>
</div>
</div>
View
135 README.rst
@@ -29,14 +29,6 @@ Quick Start, Docs, Contributing
What is django_polymorphic good for?
------------------------------------
-If you work with Django's model inheritance, django_polymorphic might
-save you from implementing unpleasant workarounds that make your code
-messy, error-prone, and slow. Model inheritance becomes much more "pythonic"
-and now just works as you as a Python programmer expect.
-
-It's best to Look at an Example
--------------------------------
-
Let's assume the models ``ArtProject`` and ``ResearchProject`` are derived
from the model ``Project``, and let's store one of each into the database:
@@ -50,62 +42,54 @@ If we want to retrieve all our projects, we do:
Using django_polymorphic, we simply get what we stored::
- [ <Project: id 1, topic: "John's Gathering">,
- <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
- <ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
+ [ <Project: id 1, topic "Weekly Office Meeting">,
+ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
+ <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
Using vanilla Django, we get incomplete objects, which is probably not what we wanted::
- [ <Project: id 1, topic: "John's Gathering">,
- <Project: id 2, topic: "Sculpting with Tim">,
- <Project: id 3, topic: "Swallow Aerodynamics"> ]
+ [ <Project: id 1, topic "Weekly Office Meeting">,
+ <Project: id 2, topic "Painting with Tim">,
+ <Project: id 3, topic "Swallow Aerodynamics"> ]
It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.
In general, the effect of django_polymorphic is twofold:
-On one hand it makes sure that model inheritance just works
-as you expect, by simply ensuring that you always get back exactly the same
-objects from the database you stored there - regardless how you access them.
-This can save you a lot of unpleasant workarounds.
+On one hand it makes sure that model inheritance just works as you
+expect, by simply ensuring that you always get back exactly thesame
+objects from the database you stored there - regardless how you access
+them, making model inheritance much more "pythonic".
+This can save you a lot of unpleasant workarounds that tend to
+make your code messy, error-prone, and slow.
-On the other hand, together with only few small API additions to the Django ORM,
-django_polymorphic enables a much more expressive and intuitive
-programming style and also very advanced object oriented
-designs that are not possible with vanilla Django.
+On the other hand, together with some small API additions to the Django
+ORM, django_polymorphic enables a much more expressive and intuitive
+programming style and also very advanced object oriented designs
+that are not possible with vanilla Django.
Fortunately, most of the heavy duty machinery that is needed for this
functionality is already present in the original Django database layer.
-Django_polymorphic merely adds a rather thin layer above that, which is
-all that is required to make real OO fully automatic and very easy to use,
-with only minimal additions to Django's API.
+Django_polymorphic adds a rather thin layer above that, which is
+all that is required to make real OO fully automatic and very easy to use.
-For more information, please look at `Quickstart`_ or the complete
-`Installation and Usage Docs`_. Please also see the `restrictions and caveats`_.
+For more information, please look at `Quickstart`_ or at the complete
+`Installation and Usage Docs`_.Please also see the `restrictions and caveats`_.
.. _restrictions and caveats: http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions
This is a V1.0 Beta/Testing Release
-----------------------------------
-This release is mostly a cleanup and maintenance release that also
-improves a number of minor things and fixes one (non-critical) bug.
-
-Some pending API changes and corrections have been folded into this release
-in order to make the upcoming V1.0 API as stable as possible.
-
-This release is also about getting feedback from you in case you don't
-approve of any of these changes or would like to get additional
-API fixes into V1.0.
-
The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
potential problems to turn up (if they exist).
-If you encounter any problems please post them in the `discussion group`_
+If you encounter any problems or have suggestions regarding the API or the
+changes in this beta, please post them in the `discussion group`_
or open an issue on GitHub_ or BitBucket_ (or send me an email).
.. _discussion group: http://groups.google.de/group/django-polymorphic/topics
@@ -117,21 +101,25 @@ License
Django_polymorphic uses the same license as Django (BSD-like).
-API Changes
-===========
+API Changes & Additions
+=======================
-October 18 2010
+
+November 01 2010, V1.0 API Changes
-------------------------------------------------------------------
-polymorphic_dumpdata
-++++++++++++++++++++
+extra() queryset method
++++++++++++++++++++++++
-The polymorphic_dumpdata management command is not needed anymore
-and has been removed, as the regular Django dumpdata command now automatically
-works correctly with polymorphic models (for all supported versions of Django).
+``.extra()`` has been re-implemented. Now it's polymorphic by
+default and works (nearly) without restrictions (please see docs). This is an
+incompatible API change regarding previous versions of django_polymorphic.
+Support for the ``polymorphic`` keyword parameter has been removed.
+You can get back the non-polymorphic behaviour by using
+``ModelA.objects.non_polymorphic().extra()``.
-Output of Queryset or Object Printing
-+++++++++++++++++++++++++++++++++++++
+No Pretty-Printing of Querysets by default
+++++++++++++++++++++++++++++++++++++++++++
In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
@@ -153,15 +141,58 @@ are now:
(the old ones still exist for compatibility)
+Pretty-Printing Output Format Changed
++++++++++++++++++++++++++++++++++++++
+
+``ShowFieldContent`` and ``ShowFieldTypeAndContent`` now
+use a slightly different output format. If this causes too much trouble for
+your test cases, you can get the old behaviour back (mostly) by adding
+``polymorphic_showfield_old_format = True`` to your model definitions.
+``ShowField...`` also produces more informative output for custom
+primary keys.
+
+polymorphic_dumpdata
+++++++++++++++++++++
+
+The ``polymorphic_dumpdata`` management command is not needed anymore
+and has been disabled, as the regular Django dumpdata command now automatically
+works correctly with polymorphic models (for all supported versions of Django).
+
Running the Test suite with Django 1.3
++++++++++++++++++++++++++++++++++++++
Django 1.3 requires ``python manage.py test polymorphic`` instead of
just ``python manage.py test``.
+November 01 2010, V1.0 API Additions
+-------------------------------------------------------------------
+
+* ``.non_polymorphic()`` queryset member function added. This is preferable to
+ using ``.base_objects...``, as it just makes the resulting queryset non-polymorphic
+ and does not change anything else in the behaviour of the manager used (while
+ ``.base_objects`` is just a different manager).
+
+* ``.get_real_instances()`` has been elevated to an official part of the API.
+ It allows you to turn a queryset or list of base objects into a list of the real instances.
+ This is useful if e.g. you use ``ModelA.objects.non_polymorphic().extra(...)`` and then want to
+ transform the result to its polymorphic equivalent:
+
+ >>> qs = ModelA.objects.all().non_polymorphic()
+ >>> real_objects = qs.get_real_instances()
+
+ is equivalent to:
+
+ >>> real_objects = ModelA.objects.all()
+
+ Instead of ``qs.get_real_instances()``, ``ModelA.objects.get_real_instances(qs)`` may be used
+ as well. In the latter case, ``qs`` may be any list of objects of type ModelA.
+
+* ``translate_polymorphic_Q_object`` (see DOCS)
+
+
February 22 2010, Installation Note
------------------------------------
+-------------------------------------------------------------------
The django_polymorphic source code has been restructured
and as a result needs to be installed like a normal Django App
@@ -185,9 +216,9 @@ imported directly from 'polymorphic' instead from
January 26 2010: Database Schema Change
------------------------------------------
+-------------------------------------------------------------------
-| The update from January 26 changed the database schema (more info in the commit-log_).
-| Sorry for any inconvenience. But this should be the final DB schema now.
+The update from January 26 changed the database schema (more info in the commit-log_).
+Sorry for any inconvenience. But this should be the final DB schema now.
.. _commit-log: http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7
View
4 pexp/management/commands/p2cmd.py
@@ -35,7 +35,11 @@ def handle_noargs(self, **options):
a=ModelA.objects.create(field1='A1')
b=ModelB.objects.create(field1='B1', field2='B2')
c=ModelC.objects.create(field1='C1', field2='C2', field3='C3')
+<<<<<<< HEAD:pexp/management/commands/p2cmd.py
print ModelA.objects.extra( select={"select1": "field1 = 'A1'", "select2": "field1 = 'A0'"} )
+=======
+ print ModelA.objects.extra( select={"select1": "field1 = 'A1'", "select2": "field1 != 'A1'"} )
+>>>>>>> 7c2be35... pexp:pexp/management/commands/p2cmd.py
print
if not 'UUIDField' in globals(): return
View
2 pexp/management/commands/polybench.py
@@ -10,7 +10,7 @@
import sys
from pexp.models import *
-num_objects=15000
+num_objects=1000
def reset_queries():
connection.queries=[]
View
2 pexp/models.py
@@ -5,7 +5,6 @@
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
-
class Project(ShowFieldContent, PolymorphicModel):
topic = models.CharField(max_length=30)
class ArtProject(Project):
@@ -27,7 +26,6 @@ class nModelB(nModelA):
class nModelC(nModelB):
field3 = models.CharField(max_length=10)
-
# for Django 1.2+, test models with same names in different apps
# (the other models with identical names are in polymorphic/tests.py)
from django import VERSION as django_VERSION
View
13 polymorphic/polymorphic_model.py
@@ -43,11 +43,10 @@ class PolymorphicModel(models.Model):
and provides a polymorphic manager as the default manager
(and as 'objects').
- PolymorphicModel overrides the save() method.
+ PolymorphicModel overrides the save() and __init__ methods.
- If your derived class overrides save() as well, then you need
- to take care that you correctly call the save() method of
- the superclass, like:
+ If your derived class overrides any of these methods as well, then you need
+ to take care that you correctly call the method of the superclass, like:
super(YourClass,self).save(*args,**kwargs)
"""
@@ -116,7 +115,7 @@ def get_real_instance(self):
def __init__(self, * args, ** kwargs):
- """Replace Django's inheritance accessors member functions for our model
+ """Replace Django's inheritance accessor member functions for our model
(self.__class__) with our own versions.
We monkey patch them until a patch can be added to Django
(which would probably be very small and make all of this obsolete).
@@ -145,7 +144,7 @@ def accessor_function(self):
return attr
return accessor_function
- subclasses_and_superclasses_accessors = self.get_inheritance_relation_fields_and_models()
+ subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models()
from django.db.models.fields.related import SingleRelatedObjectDescriptor, ReverseSingleRelatedObjectDescriptor
for name,model in subclasses_and_superclasses_accessors.iteritems():
@@ -154,7 +153,7 @@ def accessor_function(self):
#print >>sys.stderr, '---------- replacing',name, orig_accessor
setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)) )
- def get_inheritance_relation_fields_and_models(self):
+ def _get_inheritance_relation_fields_and_models(self):
"""helper function for __init__:
determine names of all Django inheritance accessor member functions for type(self)"""
View
3 polymorphic/tests.py
@@ -348,7 +348,8 @@ def show_base_manager(model):
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
# non_polymorphic()
->>> Model2A.objects.all().non_polymorphic()
+>>> qs=Model2A.objects.all().non_polymorphic()
+>>> qs
[ <Model2A: id 1, field1 (CharField)>,
<Model2A: id 2, field1 (CharField)>,
<Model2A: id 3, field1 (CharField)>,
View
13 rst-to-html.py
@@ -2,17 +2,22 @@
import sys,os
+dopart = None
+if len(sys.argv)>1: dopart = sys.argv[1]
+noshow = 'noshow' in sys.argv
+
css='--stylesheet-path=rst.css'
def conv(name):
- if len(sys.argv)>1:
+ print 'convert',name
+ if noshow:
os.system('rst2html.py '+css+' %s.rst >%s.html' % (name, name) )
else:
os.system('rst2html.py '+css+' %s.rst >%s.html ; firefox %s.html' % (name, name, name) )
-conv('DOCS')
-conv('README')
-conv('CHANGES')
+if not dopart or dopart=='1': conv('DOCS')
+if not dopart or dopart=='2': conv('README')
+if not dopart or dopart=='3': conv('CHANGES')
sys.exit()
View
11 rst.css
@@ -1,10 +1,19 @@
-h1, h2, h3, h4 {
+h1, h2, h3, h4,
+#table-of-contents
+{
color: #47c;
}
+h1 { padding-top: 15px; }
+h2 { padding-top: 10px; }
+h3 { padding-top: 7px; }
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
+li {
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
tt {
color: #080;

0 comments on commit f78b27d

Please sign in to comment.