updated docs, tests, misc
parent
52f32c1fc3
commit
f78b27dc1a
86
CHANGES.html
86
CHANGES.html
|
|
@ -11,6 +11,10 @@ h1, h2, h3, h4 {
|
||||||
color: #47c;
|
color: #47c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 { padding-top: 15px; }
|
||||||
|
h2 { padding-top: 10px; }
|
||||||
|
h3 { padding-top: 7px; }
|
||||||
|
|
||||||
a:hover { border-bottom: 1px solid #0066cc; }
|
a:hover { border-bottom: 1px solid #0066cc; }
|
||||||
a {color: #0066cc; text-decoration: none;}
|
a {color: #0066cc; text-decoration: none;}
|
||||||
|
|
||||||
|
|
@ -216,12 +220,55 @@ ul.auto-toc {
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="changelog">
|
<div class="section" id="changelog">
|
||||||
<h1>Changelog</h1>
|
<h1>Changelog</h1>
|
||||||
<p>.</p>
|
<div class="section" id="v1-0-beta-2">
|
||||||
<hr class="docutils" />
|
<h2>2010-10-25 V1.0 Beta 2</h2>
|
||||||
<div class="section" id="id1">
|
|
||||||
<h2>2010-10-18</h2>
|
|
||||||
<div class="section" id="this-is-a-v1-0-beta-testing-release">
|
<div class="section" id="this-is-a-v1-0-beta-testing-release">
|
||||||
<h3>This is a V1.0 Beta/Testing Release</h3>
|
<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">
|
||||||
|
>>> qs = ModelA.objects.all().non_polymorphic()
|
||||||
|
>>> qs.get_real_instances()
|
||||||
|
</pre>
|
||||||
|
<p>which is equivalent to:</p>
|
||||||
|
<pre class="literal-block">
|
||||||
|
>>> 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">
|
||||||
|
<h3>This is a V1.0 Beta/Testing Release</h3>
|
||||||
<p>This release is mostly a cleanup and maintenance release that also
|
<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>
|
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
|
<p>Some pending API changes and corrections have been folded into this release
|
||||||
|
|
@ -233,7 +280,7 @@ API fixes into V1.0.</p>
|
||||||
critical parts of the software. It's intended for testing and development
|
critical parts of the software. It's intended for testing and development
|
||||||
environments and not for production environments. For these, it's best to
|
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
|
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
|
<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>
|
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.
|
<p>There also have been a number of minor API changes.
|
||||||
|
|
@ -245,15 +292,14 @@ Please see the README for more information.</p>
|
||||||
<li><p class="first">official Django 1.3 alpha compatibility</p>
|
<li><p class="first">official Django 1.3 alpha compatibility</p>
|
||||||
</li>
|
</li>
|
||||||
<li><p class="first"><tt class="docutils literal">PolymorphicModel.__getattribute__</tt> hack removed.
|
<li><p class="first"><tt class="docutils literal">PolymorphicModel.__getattribute__</tt> hack removed.
|
||||||
The python __getattribute__ hack generally causes a considerable
|
This improves performance considerably as python's __getattribute__
|
||||||
overhead and to have this in the performance-sensitive PolymorphicModel
|
generally causes a pretty large processing overhead. It's gone now.</p>
|
||||||
class was somewhat problematic. It's gone for good now.</p>
|
|
||||||
</li>
|
</li>
|
||||||
<li><p class="first"><tt class="docutils literal">polymorphic_dumpdata</tt> management command functionality removed:
|
<li><p class="first">the <tt class="docutils literal">polymorphic_dumpdata</tt> management command is not needed anymore
|
||||||
The regular Django dumpdata command now automatically works correctly
|
and has been disabled, as the regular Django dumpdata command now automatically
|
||||||
for polymorphic models with all Django versions.</p>
|
works correctly with polymorphic models (for all supported versions of Django).</p>
|
||||||
</li>
|
</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">
|
<pre class="literal-block">
|
||||||
real_objects = ModelA.objects.get_real_instances(base_objects_list_or_queryset)
|
real_objects = ModelA.objects.get_real_instances(base_objects_list_or_queryset)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -278,14 +324,11 @@ Thanks to Mathieu Steele and Malthe Borch.</li>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="api-changes">
|
<div class="section" id="api-changes">
|
||||||
<h3>API Changes</h3>
|
<h3>API Changes</h3>
|
||||||
<div class="section" id="polymorphic-dumpdata">
|
<p><strong>polymorphic_dumpdata</strong></p>
|
||||||
<h4>polymorphic_dumpdata</h4>
|
<p>The management command <tt class="docutils literal">polymorphic_dumpdata</tt> is not needed anymore
|
||||||
<p>The polymorphic_dumpdata management command is not needed anymore
|
and has been disabled, as the regular Django dumpdata command now automatically
|
||||||
and has been removed, as the regular Django dumpdata command now automatically
|
|
||||||
works correctly with polymorphic models (for all supported versions of Django).</p>
|
works correctly with polymorphic models (for all supported versions of Django).</p>
|
||||||
</div>
|
<p><strong>Output of Queryset or Object Printing</strong></p>
|
||||||
<div class="section" id="output-of-queryset-or-object-printing">
|
|
||||||
<h4>Output of Queryset or Object Printing</h4>
|
|
||||||
<p>In order to improve compatibility with vanilla Django, printing quersets does not use
|
<p>In order to improve compatibility with vanilla Django, printing quersets does not use
|
||||||
django_polymorphic's pretty printing by default anymore.
|
django_polymorphic's pretty printing by default anymore.
|
||||||
To get the old behaviour when printing querysets, you need to replace your model definition:</p>
|
To get the old behaviour when printing querysets, you need to replace your model definition:</p>
|
||||||
|
|
@ -303,14 +346,11 @@ To get the old behaviour when printing querysets, you need to replace your model
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
|
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
|
||||||
<p>(the old ones still exist for compatibility)</p>
|
<p>(the old ones still exist for compatibility)</p>
|
||||||
</div>
|
<p><strong>Running the Test suite with Django 1.3</strong></p>
|
||||||
<div class="section" id="running-the-test-suite-with-django-1-3">
|
|
||||||
<h4>Running the Test suite with Django 1.3</h4>
|
|
||||||
<p>Django 1.3 requires <tt class="docutils literal">python manage.py test polymorphic</tt> instead of
|
<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>
|
just <tt class="docutils literal">python manage.py test</tt>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<hr class="docutils" />
|
<hr class="docutils" />
|
||||||
<div class="section" id="id2">
|
<div class="section" id="id2">
|
||||||
<h2>2010-2-22</h2>
|
<h2>2010-2-22</h2>
|
||||||
|
|
|
||||||
91
CHANGES.rst
91
CHANGES.rst
|
|
@ -3,12 +3,67 @@
|
||||||
Changelog
|
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
|
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
|
critical parts of the software. It's intended for testing and development
|
||||||
environments and not for production environments. For these, it's best to
|
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
|
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
|
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).
|
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
|
* official Django 1.3 alpha compatibility
|
||||||
|
|
||||||
* ``PolymorphicModel.__getattribute__`` hack removed.
|
* ``PolymorphicModel.__getattribute__`` hack removed.
|
||||||
The python __getattribute__ hack generally causes a considerable
|
This improves performance considerably as python's __getattribute__
|
||||||
overhead and to have this in the performance-sensitive PolymorphicModel
|
generally causes a pretty large processing overhead. It's gone now.
|
||||||
class was somewhat problematic. It's gone for good now.
|
|
||||||
|
|
||||||
* ``polymorphic_dumpdata`` management command functionality removed:
|
* the ``polymorphic_dumpdata`` management command is not needed anymore
|
||||||
The regular Django dumpdata command now automatically works correctly
|
and has been disabled, as the regular Django dumpdata command now automatically
|
||||||
for polymorphic models with all Django versions.
|
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)
|
real_objects = ModelA.objects.get_real_instances(base_objects_list_or_queryset)
|
||||||
|
|
||||||
|
|
@ -66,21 +120,19 @@ New Features
|
||||||
Bugfixes
|
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.
|
Thanks to Mathieu Steele and Malthe Borch.
|
||||||
|
|
||||||
API Changes
|
API Changes
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
polymorphic_dumpdata
|
**polymorphic_dumpdata**
|
||||||
####################
|
|
||||||
|
|
||||||
The polymorphic_dumpdata management command is not needed anymore
|
The management command ``polymorphic_dumpdata`` is not needed anymore
|
||||||
and has been removed, as the regular Django dumpdata command now automatically
|
and has been disabled, as the regular Django dumpdata command now automatically
|
||||||
works correctly with polymorphic models (for all supported versions of Django).
|
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
|
In order to improve compatibility with vanilla Django, printing quersets does not use
|
||||||
django_polymorphic's pretty printing by default anymore.
|
django_polymorphic's pretty printing by default anymore.
|
||||||
|
|
@ -102,8 +154,7 @@ are now:
|
||||||
|
|
||||||
(the old ones still exist for compatibility)
|
(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
|
Django 1.3 requires ``python manage.py test polymorphic`` instead of
|
||||||
just ``python manage.py test``.
|
just ``python manage.py test``.
|
||||||
|
|
|
||||||
380
DOCS.html
380
DOCS.html
|
|
@ -7,13 +7,22 @@
|
||||||
<title></title>
|
<title></title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
h1, h2, h3, h4 {
|
h1, h2, h3, h4,
|
||||||
|
#table-of-contents
|
||||||
|
{
|
||||||
color: #47c;
|
color: #47c;
|
||||||
}
|
}
|
||||||
|
h1 { padding-top: 15px; }
|
||||||
|
h2 { padding-top: 10px; }
|
||||||
|
h3 { padding-top: 7px; }
|
||||||
|
|
||||||
a:hover { border-bottom: 1px solid #0066cc; }
|
a:hover { border-bottom: 1px solid #0066cc; }
|
||||||
a {color: #0066cc; text-decoration: none;}
|
a {color: #0066cc; text-decoration: none;}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
tt {
|
tt {
|
||||||
color: #080;
|
color: #080;
|
||||||
|
|
@ -218,14 +227,15 @@ ul.auto-toc {
|
||||||
<ul class="simple">
|
<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="#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="#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="#list-of-features" id="id3">List of Features</a></li>
|
||||||
<li><a class="reference internal" href="#more-polymorphic-functionality" id="id4">More Polymorphic Functionality</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="#custom-managers-querysets-manager-inheritance" id="id5">Custom Managers, Querysets & Manager Inheritance</a></li>
|
<li><a class="reference internal" href="#more-polymorphic-functionality" id="id5">More Polymorphic Functionality</a></li>
|
||||||
<li><a class="reference internal" href="#performance-considerations" id="id6">Performance Considerations</a></li>
|
<li><a class="reference internal" href="#custom-managers-querysets-manager-inheritance" id="id6">Custom Managers, Querysets & Manager Inheritance</a></li>
|
||||||
<li><a class="reference internal" href="#possible-optimizations" id="id7">Possible Optimizations</a></li>
|
<li><a class="reference internal" href="#performance-considerations" id="id7">Performance Considerations</a></li>
|
||||||
<li><a class="reference internal" href="#restrictions-caveats" id="id8">Restrictions & Caveats</a></li>
|
<li><a class="reference internal" href="#possible-optimizations" id="id8">Possible Optimizations</a></li>
|
||||||
<li><a class="reference internal" href="#project-status" id="id9">Project Status</a></li>
|
<li><a class="reference internal" href="#restrictions-caveats" id="id9">Restrictions & Caveats</a></li>
|
||||||
<li><a class="reference internal" href="#links" id="id10">Links</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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -233,7 +243,7 @@ ul.auto-toc {
|
||||||
<h1><a class="toc-backref" href="#id2">Quickstart</a></h1>
|
<h1><a class="toc-backref" href="#id2">Quickstart</a></h1>
|
||||||
<div class="section" id="install">
|
<div class="section" id="install">
|
||||||
<h2>Install</h2>
|
<h2>Install</h2>
|
||||||
<p>After uncompressing (if necessary), in the directory "django_polymorphic",
|
<p>After uncompressing (if necessary), in the directory "...django_polymorphic",
|
||||||
execute (on Unix-like systems):</p>
|
execute (on Unix-like systems):</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
sudo python setup.py install
|
sudo python setup.py install
|
||||||
|
|
@ -268,57 +278,66 @@ class ResearchProject(Project):
|
||||||
<h2>Get polymorphic query results</h2>
|
<h2>Get polymorphic query results</h2>
|
||||||
<pre class="doctest-block">
|
<pre class="doctest-block">
|
||||||
>>> Project.objects.all()
|
>>> Project.objects.all()
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
[ <Project: id 1, topic "John's Gathering">,
|
||||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
</pre>
|
</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">
|
<pre class="doctest-block">
|
||||||
>>> Project.objects.instance_of(ArtProject)
|
>>> 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"> ]
|
||||||
</pre>
|
</pre>
|
||||||
<pre class="doctest-block">
|
<pre class="doctest-block">
|
||||||
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
|
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
|
||||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
</pre>
|
</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>
|
or supervisor (note the three underscores):</p>
|
||||||
<pre class="doctest-block">
|
<pre class="doctest-block">
|
||||||
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
|
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
|
||||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "History of Sculpting", supervisor: "T. Turner"> ]
|
<ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
<p>This is basically all you need to know, as django_polymorphic mostly
|
||||||
<div class="section" id="what-s-more">
|
works fully automatic and just delivers the expected ("pythonic") results.</p>
|
||||||
<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 "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).</p>
|
|
||||||
<p>Note: In all example output, above and below, for a nicer and more informative
|
<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>
|
output the <cite>ShowFieldType</cite> mixin has been used (documented below).</p>
|
||||||
</div>
|
</div>
|
||||||
</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 ("ArtProject___artist")</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 ("qs3 = qs1 | qs2")</li>
|
||||||
|
<li>Nice/informative display of polymorphic queryset results</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="section" id="more-about-installation-testing">
|
<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">
|
<div class="section" id="requirements">
|
||||||
<h2>Requirements</h2>
|
<h2>Requirements</h2>
|
||||||
<p>Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
|
<p>Django 1.1 (or later) and Python 2.4 or later. 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>
|
on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="included-test-suite">
|
<div class="section" id="included-test-suite">
|
||||||
<h2>Included Test Suite</h2>
|
<h2>Included Test Suite</h2>
|
||||||
<p>The repository (or tar file) contains a complete Django project
|
<p>The repository (or tar file) contains a complete Django project
|
||||||
that may be used for tests or experiments, without any installation needed.</p>
|
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 "...django_polymorphic" execute:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
./manage test polymorphic
|
./manage test polymorphic
|
||||||
</pre>
|
</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
|
<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used
|
||||||
experiments - modify this file (pexp/management/commands/pcmd.py)
|
for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
|
||||||
to your liking, then run:</p>
|
to your liking, then run:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
||||||
|
|
@ -327,19 +346,18 @@ to your liking, then run:</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="installation">
|
<div class="section" id="installation">
|
||||||
<h2>Installation</h2>
|
<h2>Installation</h2>
|
||||||
<p>In the directory "django_polymorphic", execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
|
<p>In the directory "...django_polymorphic", 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>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> subdirectory
|
||||||
(under "django_polymorphic") into your Django project dir
|
(under "django_polymorphic") into your Django project dir
|
||||||
(e.g. if you want to distribute your project with more 'batteries included').</p>
|
(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
|
<p>If you want to run the test cases in <cite>polymorphic/tests.py</cite>, you need to add
|
||||||
you need to add <tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting. This is also
|
<tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting.</p>
|
||||||
needed if you want to run the test cases in <cite>polymorphic/tests.py</cite>.</p>
|
<p>Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
|
||||||
<p>In any case, Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
|
|
||||||
needs to be listed in INSTALLED_APPS (usually it already is).</p>
|
needs to be listed in INSTALLED_APPS (usually it already is).</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="more-polymorphic-functionality">
|
<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>
|
<p>In the examples below, these models are being used:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
from polymorphic import PolymorphicModel
|
from polymorphic import PolymorphicModel
|
||||||
|
|
@ -383,12 +401,12 @@ syntax: <tt class="docutils literal">exact model name + three _ + field name</tt
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="combining-querysets-of-different-types-models">
|
<div class="section" id="combining-querysets-querysets-as-object-containers">
|
||||||
<h2>Combining Querysets of different types/models</h2>
|
<h2>Combining Querysets / Querysets as "Object Containers"</h2>
|
||||||
<p>Querysets may now be regarded as object containers that allow the
|
<p>Querysets could now be regarded as object containers that allow the
|
||||||
aggregation of different object types - very similar to python
|
aggregation of different object types, very similar to python
|
||||||
lists (as long as the objects are accessed through the manager of
|
lists - as long as the objects are accessed through the manager of
|
||||||
a common base class):</p>
|
a common base class:</p>
|
||||||
<pre class="doctest-block">
|
<pre class="doctest-block">
|
||||||
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
|
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
|
||||||
.
|
.
|
||||||
|
|
@ -432,31 +450,43 @@ model as the root of a polymorphic inheritance tree:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
from thirdparty import ThirdPartyModel
|
from thirdparty import ThirdPartyModel
|
||||||
|
|
||||||
class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
|
class MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
|
||||||
pass # or add fields
|
pass # or add fields
|
||||||
</pre>
|
</pre>
|
||||||
<p>Or instead integrating the third party model anywhere into an
|
<p>Or instead integrating the third party model anywhere into an
|
||||||
existing polymorphic inheritance tree:</p>
|
existing polymorphic inheritance tree:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
class MyModel(SomePolymorphicModel):
|
class MyBaseModel(SomePolymorphicModel):
|
||||||
my_field = models.CharField(max_length=10)
|
my_field = models.CharField(max_length=10)
|
||||||
|
|
||||||
class MyModelWithThirdParty(MyModel, ThirdPartyModel):
|
class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
|
||||||
pass # or add fields
|
pass # or add fields
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="non-polymorphic-queries">
|
<div class="section" id="non-polymorphic-queries">
|
||||||
<h2>Non-Polymorphic Queries</h2>
|
<h2>Non-Polymorphic Queries</h2>
|
||||||
<pre class="doctest-block">
|
<pre class="doctest-block">
|
||||||
>>> ModelA.base_objects.all()
|
>>> qs=ModelA.objects.non_polymorphic().all()
|
||||||
|
>>> qs
|
||||||
.
|
.
|
||||||
[ <ModelA: id 1, field1 (CharField)>,
|
[ <ModelA: id 1, field1 (CharField)>,
|
||||||
<ModelA: id 2, field1 (CharField)>,
|
<ModelA: id 2, field1 (CharField)>,
|
||||||
<ModelA: id 3, field1 (CharField)> ]
|
<ModelA: id 3, field1 (CharField)> ]
|
||||||
</pre>
|
</pre>
|
||||||
<p>Each polymorphic model has 'base_objects' defined as a normal
|
<p>If you insert <tt class="docutils literal">.non_polymorphic()</tt> anywhere into the query chain, then
|
||||||
Django manager. Of course, arbitrary custom managers may be
|
django_polymorphic will simply leave out the final step of retrieving the
|
||||||
added to the models as well.</p>
|
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">
|
||||||
|
>>> qs.get_real_instances()
|
||||||
|
[ <ModelA: id 1, field1 (CharField)>,
|
||||||
|
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
|
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="about-queryset-methods">
|
<div class="section" id="about-queryset-methods">
|
||||||
<h2>About Queryset Methods</h2>
|
<h2>About Queryset Methods</h2>
|
||||||
|
|
@ -464,43 +494,29 @@ added to the models as well.</p>
|
||||||
<li><tt class="docutils literal">annotate()</tt> and <tt class="docutils literal">aggregate()</tt> work just as usual, with the
|
<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
|
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>
|
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
|
<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>
|
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
|
<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>
|
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
|
<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
|
to select relations in derived models
|
||||||
(like <tt class="docutils literal"><span class="pre">ModelA.objects.select_related('ModelC___fieldxy')</span></tt> )</li>
|
(like <tt class="docutils literal"><span class="pre">ModelA.objects.select_related('ModelC___fieldxy')</span></tt> )</li>
|
||||||
</ul>
|
<li><tt class="docutils literal">extra()</tt> works as expected (it returns polymorphic results) but
|
||||||
<ul class="simple">
|
currently has one restriction: The resulting objects are required to have
|
||||||
<li><tt class="docutils literal">extra()</tt> by default works exactly like the original version,
|
a unique primary key within the result set - otherwise an error is thrown
|
||||||
with the resulting queryset not being polymorphic. There is
|
(this case could be made to work, however it may be mostly unneeded)..
|
||||||
experimental support for a polymorphic extra() via the keyword
|
The keyword-argument "polymorphic" is no longer supported.
|
||||||
argument <tt class="docutils literal">polymorphic=True</tt> (only the <tt class="docutils literal">where</tt> and
|
You can get back the old non-polymorphic behaviour (before V1.0)
|
||||||
<tt class="docutils literal">order_by</tt> and <tt class="docutils literal">params</tt> arguments of extra() should be used then).
|
by using <tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra(...)</span></tt>.</li>
|
||||||
The behaviour of extra() may change in the future, so it's best if you use
|
<li><tt class="docutils literal">get_real_instances()</tt> allows you to turn a
|
||||||
<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
|
|
||||||
queryset or list of base model objects efficiently into the real objects.
|
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
|
For example, you could do <tt class="docutils literal"><span class="pre">base_objects_queryset=ModelA.extra(...).non_polymorphic()</span></tt>
|
||||||
then call <tt class="docutils literal">real_objects=ModelA.objects.get_real_instances(base_objects)</tt>.</li>
|
and then call <tt class="docutils literal">real_objects=base_objects_queryset.get_real_instances()</tt>.Or alternatively
|
||||||
</ul>
|
.``real_objects=ModelA.objects..get_real_instances(base_objects_queryset_or_object_list)``</li>
|
||||||
<ul class="simple">
|
|
||||||
<li><tt class="docutils literal">values()</tt> & <tt class="docutils literal">values_list()</tt> currently do not return polymorphic
|
<li><tt class="docutils literal">values()</tt> & <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
|
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
|
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>
|
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
|
<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>
|
in the future).</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -537,27 +553,40 @@ class ModelA(ShowFieldType, PolymorphicModel):
|
||||||
</pre>
|
</pre>
|
||||||
<p>You may also use ShowFieldContent or ShowFieldTypeAndContent to display
|
<p>You may also use ShowFieldContent or ShowFieldTypeAndContent to display
|
||||||
additional information when printing querysets (or converting them to text).</p>
|
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>
|
</div>
|
||||||
<div class="section" id="custom-managers-querysets-manager-inheritance">
|
<div class="section" id="custom-managers-querysets-manager-inheritance">
|
||||||
<h1><a class="toc-backref" href="#id5">Custom Managers, Querysets & Manager Inheritance</a></h1>
|
<h1><a class="toc-backref" href="#id6">Custom Managers, Querysets & Manager Inheritance</a></h1>
|
||||||
<div class="section" id="using-a-custom-manager">
|
<div class="section" id="using-a-custom-manager">
|
||||||
<h2>Using a Custom Manager</h2>
|
<h2>Using a Custom Manager</h2>
|
||||||
<p>A nice feature of Django is the possibility to define one's own custom object managers.
|
<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
|
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
|
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>
|
explicitly add the default manager first, and then your custom manager:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||||
|
|
||||||
class MyOrderedManager(PolymorphicManager):
|
class TimeOrderedManager(PolymorphicManager):
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
return super(MyOrderedManager,self).get_query_set().order_by('some_field')
|
qs = super(TimeOrderedManager,self).get_query_set()
|
||||||
|
return qs.order_by('-start_date') # order the queryset
|
||||||
|
|
||||||
class MyModel(PolymorphicModel):
|
def most_recent(self):
|
||||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
qs = self.get_query_set() # get my ordered queryset
|
||||||
ordered_objects = MyOrderedManager() # then add your own manager
|
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
|
||||||
</pre>
|
</pre>
|
||||||
<p>The first manager defined ('objects' in the example) is used by
|
<p>The first manager defined ('objects' in the example) is used by
|
||||||
Django as automatic manager for several purposes, including accessing
|
Django as automatic manager for several purposes, including accessing
|
||||||
|
|
@ -568,18 +597,33 @@ the plain <tt class="docutils literal">PolymorphicManager</tt> here.</p>
|
||||||
<h2>Manager Inheritance</h2>
|
<h2>Manager Inheritance</h2>
|
||||||
<p>Polymorphic models inherit/propagate all managers from their
|
<p>Polymorphic models inherit/propagate all managers from their
|
||||||
base models, as long as these are polymorphic. This means that all
|
base models, as long as these are polymorphic. This means that all
|
||||||
managers defined in polymorphic base models work just the same as if
|
managers defined in polymorphic base models continue to work as
|
||||||
they were defined in the new model.</p>
|
expected in models inheriting from this base model:</p>
|
||||||
<p>An example (inheriting from MyModel above):</p>
|
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
class MyModel2(MyModel):
|
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||||
pass
|
|
||||||
|
|
||||||
# Managers inherited from MyModel:
|
class TimeOrderedManager(PolymorphicManager):
|
||||||
# the regular 'objects' manager and the custom 'ordered_objects' manager
|
def get_query_set(self):
|
||||||
>>> MyModel2.objects.all()
|
qs = super(TimeOrderedManager,self).get_query_set()
|
||||||
>>> MyModel2.ordered_objects.all()
|
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
|
||||||
|
|
||||||
|
class ArtProject(Project): # inherit from Project, inheriting its fields and managers
|
||||||
|
artist = models.CharField(max_length=30)
|
||||||
</pre>
|
</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>
|
||||||
<div class="section" id="using-a-custom-queryset-class">
|
<div class="section" id="using-a-custom-queryset-class">
|
||||||
<h2>Using a Custom Queryset Class</h2>
|
<h2>Using a Custom Queryset Class</h2>
|
||||||
|
|
@ -601,10 +645,16 @@ class MyModel(PolymorphicModel):
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="performance-considerations">
|
<div class="section" id="performance-considerations">
|
||||||
<h1><a class="toc-backref" href="#id6">Performance Considerations</a></h1>
|
<h1><a class="toc-backref" href="#id7">Performance Considerations</a></h1>
|
||||||
<p>The current implementation is pretty simple and does not use any
|
<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
|
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">
|
<pre class="literal-block">
|
||||||
result_objects = list( ModelA.objects.filter(...) )
|
result_objects = list( ModelA.objects.filter(...) )
|
||||||
</pre>
|
</pre>
|
||||||
|
|
@ -615,41 +665,58 @@ class <tt class="docutils literal">ModelA</tt>. If 50 objects are <tt class="doc
|
||||||
two queries are executed. The pathological worst case is 101 db queries if
|
two queries are executed. The pathological worst case is 101 db queries if
|
||||||
result_objects contains 100 different object types (with all of them
|
result_objects contains 100 different object types (with all of them
|
||||||
subclasses of <tt class="docutils literal">ModelA</tt>).</p>
|
subclasses of <tt class="docutils literal">ModelA</tt>).</p>
|
||||||
<p>Performance ist relative: when Django users create their own
|
<p>Usually, when Django users create their own polymorphic ad-hoc solution
|
||||||
polymorphic ad-hoc solution (without a tool like <tt class="docutils literal">django_polymorphic</tt>),
|
without a tool like django_polymorphic, this usually results in a variation of</p>
|
||||||
this usually results in a variation of</p>
|
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
||||||
</pre>
|
</pre>
|
||||||
<p>which has really bad performance. Relative to this, the
|
<p>which has exceptionally bad performance, as it introduces one additional
|
||||||
performance of the current <tt class="docutils literal">django_polymorphic</tt> is pretty good.
|
SQL query for every object in the result which is not of class <tt class="docutils literal">BaseModel</tt>.
|
||||||
It's probably efficient enough for the majority of use cases.</p>
|
Relative to this, the performance of the current django_polymorphic
|
||||||
<p>Chunking: The implementation always requests objects in chunks of
|
implementation is very good.</p>
|
||||||
size <tt class="docutils literal">Polymorphic_QuerySet_objects_per_request</tt>. This limits the
|
<p>If your project however needs perfect performance and the current
|
||||||
complexity/duration for each query, including the pathological cases.</p>
|
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>
|
||||||
<div class="section" id="possible-optimizations">
|
<div class="section" id="possible-optimizations">
|
||||||
<h1><a class="toc-backref" href="#id7">Possible Optimizations</a></h1>
|
<h1><a class="toc-backref" href="#id8">Possible Optimizations</a></h1>
|
||||||
<p><tt class="docutils literal">PolymorphicQuerySet</tt> can be optimized to require only one SQL query
|
<p>Django_polymorphic can be optimized to require only one
|
||||||
for the queryset evaluation and retrieval of all objects.</p>
|
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
|
<p>Probably all that would be needed seems support for an additional
|
||||||
from all relevant sub-models with one SQL query. However, some deeper
|
queryset function in Django's database layer, like:</p>
|
||||||
digging into the Django database layer will be required in order to
|
<pre class="literal-block">
|
||||||
make this happen.</p>
|
ModelA.objects.join_models(on="field_name", models=[ModelB, ModelC])
|
||||||
<p>An optimized version might require an SQL database. For non-SQL databases
|
</pre>
|
||||||
the implementation could fall back to the current ORM-only
|
<p>or, less general but more simple:</p>
|
||||||
implementation.</p>
|
<pre class="literal-block">
|
||||||
|
ModelA.objects.join_tables(on="field_name", 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">
|
<div class="section" id="sql-complexity-of-an-optimized-implementation">
|
||||||
<h2>SQL Complexity of an Optimized Implementation</h2>
|
<h2>SQL Complexity of an Optimized Implementation</h2>
|
||||||
<p>With only one SQL query, one SQL join for each possible subclass
|
<p>With only one SQL query, one SQL join for each possible subclass
|
||||||
would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).
|
would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).</p>
|
||||||
With two SQL queries, the number of joins could be reduced to the
|
<p>With two SQL queries, the number of joins could be reduced to the
|
||||||
number of actuallly occurring subclasses in the result. A final
|
number of actuallly occurring subclasses in the specific result.</p>
|
||||||
implementation might want to use one query only if the number of
|
<p>A perfect implementation might want to use one query only
|
||||||
possible subclasses (and therefore joins) is not too large, and
|
if the number of possible subclasses (and therefore joins) is not
|
||||||
two queries otherwise (using the first query to determine the
|
too large, and two queries otherwise (using the first query to
|
||||||
actually occurring subclasses, reducing the number of joins for
|
determine the actually occurring subclasses, reducing the number
|
||||||
the second).</p>
|
of joins for the second).</p>
|
||||||
<p>The number of joins needed for polymorphic object retrieval might
|
<p>The number of joins needed for polymorphic object retrieval might
|
||||||
raise concerns regarding the efficiency of these database
|
raise concerns regarding the efficiency of these database
|
||||||
queries. It seems likely however, that the increased number of joins
|
queries. It seems likely however, that the increased number of joins
|
||||||
|
|
@ -657,30 +724,21 @@ 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
|
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,
|
be problematic, it is possible to split any problematic query into, for example,
|
||||||
two queries with only half the number of joins each.</p>
|
two queries with only half the number of joins each.</p>
|
||||||
</div>
|
<p>It seems that further optimization (down to one DB request)
|
||||||
<div class="section" id="in-general">
|
of django_polymorphic would be restricted to a relatively small area of
|
||||||
<h2>In General</h2>
|
the code ("query.py"), and be pretty much independent from the rest of the module.
|
||||||
<p>Let's not forget that the above is just about optimization.
|
Such an optimization can be done at any later time (like when it's needed).</p>
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="restrictions-caveats">
|
<div class="section" id="restrictions-caveats">
|
||||||
<span id="restrictions"></span><h1><a class="toc-backref" href="#id8">Restrictions & Caveats</a></h1>
|
<span id="restrictions"></span><h1><a class="toc-backref" href="#id9">Restrictions & 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>
|
|
||||||
<ul class="simple">
|
<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,
|
<li>Django Admin Integration: There currently is no specific admin integration,
|
||||||
but it would most likely make sense to have one.</li>
|
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
|
<li>Diamond shaped inheritance: There seems to be a general problem
|
||||||
with diamond shaped multiple model inheritance with Django models
|
with diamond shaped multiple model inheritance with Django models
|
||||||
(tested with V1.1 - V1.3).
|
(tested with V1.1 - V1.3).
|
||||||
|
|
@ -688,19 +746,21 @@ An example is here: <a class="reference external" href="http://code.djangoprojec
|
||||||
This problem is aggravated when trying to enhance models.Model
|
This problem is aggravated when trying to enhance models.Model
|
||||||
by subclassing it instead of modifying Django core (as we do here
|
by subclassing it instead of modifying Django core (as we do here
|
||||||
with PolymorphicModel).</li>
|
with PolymorphicModel).</li>
|
||||||
</ul>
|
|
||||||
<ul class="simple">
|
|
||||||
<li>The enhanced filter-definitions/Q-objects only work as arguments
|
<li>The enhanced filter-definitions/Q-objects only work as arguments
|
||||||
for the methods of the polymorphic querysets. Please see above
|
for the methods of the polymorphic querysets. Please see above
|
||||||
for <tt class="docutils literal">translate_polymorphic_Q_object</tt>.</li>
|
for <tt class="docutils literal">translate_polymorphic_Q_object</tt>.</li>
|
||||||
</ul>
|
<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
|
||||||
<ul class="simple">
|
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
|
<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.
|
in the whole project, even if they are in two different apps.
|
||||||
This results from a restriction in the Django 1.1 "related_name"
|
This results from a restriction in the Django 1.1 "related_name"
|
||||||
option (fixed in Django 1.2).</li>
|
option (fixed in Django 1.2).</li>
|
||||||
</ul>
|
|
||||||
<ul class="simple">
|
|
||||||
<li>Django 1.1 only - when ContentType is used in models, Django's
|
<li>Django 1.1 only - when ContentType is used in models, Django's
|
||||||
seralisation or fixtures cannot be used (all polymorphic models
|
seralisation or fixtures cannot be used (all polymorphic models
|
||||||
use ContentType). This issue seems to be resolved for Django 1.2
|
use ContentType). This issue seems to be resolved for Django 1.2
|
||||||
|
|
@ -710,25 +770,15 @@ use ContentType). This issue seems to be resolved for Django 1.2
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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>
|
||||||
<div class="section" id="project-status">
|
<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,
|
<p>Django_polymorphic works well for a considerable number of users now,
|
||||||
and no major problems have shown up for many months.
|
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>
|
||||||
<div class="section" id="links">
|
<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">
|
<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://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>
|
<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>
|
||||||
|
|
|
||||||
307
DOCS.rst
307
DOCS.rst
|
|
@ -11,7 +11,7 @@ Quickstart
|
||||||
Install
|
Install
|
||||||
-------
|
-------
|
||||||
|
|
||||||
After uncompressing (if necessary), in the directory "django_polymorphic",
|
After uncompressing (if necessary), in the directory "...django_polymorphic",
|
||||||
execute (on Unix-like systems)::
|
execute (on Unix-like systems)::
|
||||||
|
|
||||||
sudo python setup.py install
|
sudo python setup.py install
|
||||||
|
|
@ -37,47 +37,61 @@ All models inheriting from your polymorphic models will be polymorphic as well.
|
||||||
Create some objects
|
Create some objects
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
>>> Project.objects.create(topic="John's Gathering")
|
>>> Project.objects.create(topic="Office Meeting")
|
||||||
>>> ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
|
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
|
||||||
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
||||||
|
|
||||||
Get polymorphic query results
|
Get polymorphic query results
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
>>> Project.objects.all()
|
>>> Project.objects.all()
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
[ <Project: id 1, topic "John's Gathering">,
|
||||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<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)
|
>>> 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)
|
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
|
||||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<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):
|
or supervisor (note the three underscores):
|
||||||
|
|
||||||
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
|
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
|
||||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "History of Sculpting", supervisor: "T. Turner"> ]
|
<ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
|
||||||
|
|
||||||
What's More?
|
This is basically all you need to know, as django_polymorphic mostly
|
||||||
-------------
|
works fully automatic and just delivers the expected ("pythonic") results.
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
Note: In all example output, above and below, for a nicer and more informative
|
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
|
More about Installation / Testing
|
||||||
|
|
@ -86,8 +100,8 @@ More about Installation / Testing
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
|
Django 1.1 (or later) and Python 2.4 or later. 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.
|
on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.
|
||||||
|
|
||||||
Included Test Suite
|
Included Test Suite
|
||||||
-------------------
|
-------------------
|
||||||
|
|
@ -95,12 +109,12 @@ Included Test Suite
|
||||||
The repository (or tar file) contains a complete Django project
|
The repository (or tar file) contains a complete Django project
|
||||||
that may be used for tests or experiments, without any installation needed.
|
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
|
./manage test polymorphic
|
||||||
|
|
||||||
The management command ``pcmd.py`` in the app ``pexp`` can be used for
|
The management command ``pcmd.py`` in the app ``pexp`` can be used
|
||||||
experiments - modify this file (pexp/management/commands/pcmd.py)
|
for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
|
||||||
to your liking, then run::
|
to your liking, then run::
|
||||||
|
|
||||||
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
||||||
|
|
@ -109,17 +123,16 @@ to your liking, then run::
|
||||||
Installation
|
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
|
(under "django_polymorphic") into your Django project dir
|
||||||
(e.g. if you want to distribute your project with more 'batteries included').
|
(e.g. if you want to distribute your project with more 'batteries included').
|
||||||
|
|
||||||
If you want to use the management command ``polymorphic_dumpdata``, then
|
If you want to run the test cases in `polymorphic/tests.py`, you need to add
|
||||||
you need to add ``polymorphic`` to your INSTALLED_APPS setting. This is also
|
``polymorphic`` to your INSTALLED_APPS setting.
|
||||||
needed if you want to run the test cases in `polymorphic/tests.py`.
|
|
||||||
|
|
||||||
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).
|
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):
|
class ModelC(ModelB):
|
||||||
field3 = models.CharField(max_length=10)
|
field3 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
|
||||||
Filtering for classes (equivalent to python's isinstance() ):
|
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) )
|
>>> ModelA.objects.filter( Q(instance_of=ModelB) )
|
||||||
|
|
||||||
|
|
||||||
Polymorphic filtering (for fields in derived classes)
|
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)>,
|
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (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
|
Combining Querysets / Querysets as "Object Containers"
|
||||||
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):
|
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)
|
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
|
||||||
.
|
.
|
||||||
[ <ModelX: id 1, field_x (CharField)>,
|
[ <ModelX: id 1, field_x (CharField)>,
|
||||||
<ModelY: id 2, field_y (CharField)> ]
|
<ModelY: id 2, field_y (CharField)> ]
|
||||||
|
|
||||||
|
|
||||||
ManyToManyField, ForeignKey, OneToOneField
|
ManyToManyField, ForeignKey, OneToOneField
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
|
|
@ -211,6 +228,7 @@ A ManyToManyField example::
|
||||||
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
|
|
||||||
Using Third Party Models (without modifying them)
|
Using Third Party Models (without modifying them)
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -220,18 +238,19 @@ model as the root of a polymorphic inheritance tree::
|
||||||
|
|
||||||
from thirdparty import ThirdPartyModel
|
from thirdparty import ThirdPartyModel
|
||||||
|
|
||||||
class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
|
class MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
|
||||||
pass # or add fields
|
pass # or add fields
|
||||||
|
|
||||||
Or instead integrating the third party model anywhere into an
|
Or instead integrating the third party model anywhere into an
|
||||||
existing polymorphic inheritance tree::
|
existing polymorphic inheritance tree::
|
||||||
|
|
||||||
class MyModel(SomePolymorphicModel):
|
class MyBaseModel(SomePolymorphicModel):
|
||||||
my_field = models.CharField(max_length=10)
|
my_field = models.CharField(max_length=10)
|
||||||
|
|
||||||
class MyModelWithThirdParty(MyModel, ThirdPartyModel):
|
class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
|
||||||
pass # or add fields
|
pass # or add fields
|
||||||
|
|
||||||
|
|
||||||
Non-Polymorphic Queries
|
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
|
the base class you used for the query, like vanilla Django would
|
||||||
(``ModelA`` in this example).
|
(``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,
|
There are no other changes in the behaviour of the queryset. For example,
|
||||||
enhancements for ``filter()`` or ``instance_of()`` etc. still work as expected.
|
enhancements for ``filter()`` or ``instance_of()`` etc. still work as expected.
|
||||||
If you do the final step yourself, you get the usual polymorphic result:
|
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)>,
|
[ <ModelA: id 1, field1 (CharField)>,
|
||||||
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (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
|
addition that the ``ModelX___field`` syntax can be used for the
|
||||||
keyword arguments (but not for the non-keyword arguments).
|
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.
|
for specifying ordering through a field in a submodel.
|
||||||
|
|
||||||
* ``distinct()`` works as expected. It only regards the fields of
|
* ``distinct()`` works as expected. It only regards the fields of
|
||||||
the base class, but this should never make a difference.
|
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
|
to select relations in derived models
|
||||||
(like ``ModelA.objects.select_related('ModelC___fieldxy')`` )
|
(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
|
currently has one restriction: The resulting objects are required to have
|
||||||
a unique primary key within the result set - otherwise an error is thrown
|
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)..
|
(this case could be made to work, however it may be mostly unneeded)..
|
||||||
The keyword-argument "polymorphic" is no longer supported.
|
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.
|
queryset or list of base model objects efficiently into the real objects.
|
||||||
For example, you could do ``base_objects_queryset=ModelA.extra(...).non_polymorphic()``
|
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
|
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
|
methods now, it's best if you use ``Model.base_objects.values...`` as
|
||||||
this is guaranteed to not change.
|
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).
|
in the future).
|
||||||
|
|
||||||
|
|
||||||
Using enhanced Q-objects in any Places
|
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,
|
somekey = model.ForeignKey(Model2A,
|
||||||
limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) )
|
limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) )
|
||||||
|
|
||||||
|
|
||||||
Nicely Displaying Polymorphic Querysets
|
Nicely Displaying Polymorphic Querysets
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
|
@ -322,6 +351,15 @@ ShowFieldType class mixin::
|
||||||
You may also use ShowFieldContent or ShowFieldTypeAndContent to display
|
You may also use ShowFieldContent or ShowFieldTypeAndContent to display
|
||||||
additional information when printing querysets (or converting them to text).
|
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
|
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.
|
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
|
This is fully supported with django_polymorphic: For creating a custom polymorphic
|
||||||
manager class, just derive your manager from ``PolymorphicManager`` instead of
|
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::
|
explicitly add the default manager first, and then your custom manager::
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||||
|
|
||||||
class MyOrderedManager(PolymorphicManager):
|
class TimeOrderedManager(PolymorphicManager):
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
return super(MyOrderedManager,self).get_query_set().order_by('some_field')
|
qs = super(TimeOrderedManager,self).get_query_set()
|
||||||
|
return qs.order_by('-start_date') # order the queryset
|
||||||
|
|
||||||
class MyModel(PolymorphicModel):
|
def most_recent(self):
|
||||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
qs = self.get_query_set() # get my ordered queryset
|
||||||
ordered_objects = MyOrderedManager() # then add your own manager
|
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
|
The first manager defined ('objects' in the example) is used by
|
||||||
Django as automatic manager for several purposes, including accessing
|
Django as automatic manager for several purposes, including accessing
|
||||||
|
|
@ -355,18 +399,34 @@ Manager Inheritance
|
||||||
|
|
||||||
Polymorphic models inherit/propagate all managers from their
|
Polymorphic models inherit/propagate all managers from their
|
||||||
base models, as long as these are polymorphic. This means that all
|
base models, as long as these are polymorphic. This means that all
|
||||||
managers defined in polymorphic base models work just the same as if
|
managers defined in polymorphic base models continue to work as
|
||||||
they were defined in the new model.
|
expected in models inheriting from this base model::
|
||||||
|
|
||||||
An example (inheriting from MyModel above)::
|
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||||
|
|
||||||
class MyModel2(MyModel):
|
class TimeOrderedManager(PolymorphicManager):
|
||||||
pass
|
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:
|
def most_recent(self):
|
||||||
# the regular 'objects' manager and the custom 'ordered_objects' manager
|
qs = self.get_query_set() # get my ordered queryset
|
||||||
>>> MyModel2.objects.all()
|
return qs[:10] # limit => get ten most recent entries
|
||||||
>>> 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)
|
||||||
|
|
||||||
|
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
|
Using a Custom Queryset Class
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
@ -390,9 +450,18 @@ instead of Django's QuerySet as the base class::
|
||||||
Performance Considerations
|
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
|
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(...) )
|
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
|
result_objects contains 100 different object types (with all of them
|
||||||
subclasses of ``ModelA``).
|
subclasses of ``ModelA``).
|
||||||
|
|
||||||
Performance ist relative: when Django users create their own
|
Usually, when Django users create their own polymorphic ad-hoc solution
|
||||||
polymorphic ad-hoc solution (without a tool like ``django_polymorphic``),
|
without a tool like django_polymorphic, this usually results in a variation of ::
|
||||||
this usually results in a variation of ::
|
|
||||||
|
|
||||||
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
||||||
|
|
||||||
which has really bad performance. Relative to this, the
|
which has exceptionally bad performance, as it introduces one additional
|
||||||
performance of the current ``django_polymorphic`` is pretty good.
|
SQL query for every object in the result which is not of class ``BaseModel``.
|
||||||
It's probably efficient enough for the majority of use cases.
|
Relative to this, the performance of the current django_polymorphic
|
||||||
|
implementation is very good.
|
||||||
|
|
||||||
Chunking: The implementation always requests objects in chunks of
|
If your project however needs perfect performance and the current
|
||||||
size ``Polymorphic_QuerySet_objects_per_request``. This limits the
|
performance implications of django_polymorphic are not acceptable, then
|
||||||
complexity/duration for each query, including the pathological cases.
|
basically there are the two options of either foregoing of an essential aspect
|
||||||
|
of object oriented programming or optimizing django_polymorphic.
|
||||||
|
|
||||||
|
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
|
Possible Optimizations
|
||||||
======================
|
======================
|
||||||
|
|
||||||
``PolymorphicQuerySet`` can be optimized to require only one SQL query
|
Django_polymorphic can be optimized to require only one
|
||||||
for the queryset evaluation and retrieval of all objects.
|
SQL query for the queryset evaluation and retrieval of all objects.
|
||||||
|
|
||||||
Basically, what ist needed is a possibility to pull in the fields
|
Probably all that would be needed seems support for an additional
|
||||||
from all relevant sub-models with one SQL query. However, some deeper
|
queryset function in Django's database layer, like::
|
||||||
digging into the Django database layer will be required in order to
|
|
||||||
make this happen.
|
|
||||||
|
|
||||||
An optimized version might require an SQL database. For non-SQL databases
|
ModelA.objects.join_models(on="field_name", models=[ModelB, ModelC])
|
||||||
the implementation could fall back to the current ORM-only
|
|
||||||
implementation.
|
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
|
SQL Complexity of an Optimized Implementation
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
With only one SQL query, one SQL join for each possible subclass
|
With only one SQL query, one SQL join for each possible subclass
|
||||||
would be needed (``BaseModel.__subclasses__()``, recursively).
|
would be needed (``BaseModel.__subclasses__()``, recursively).
|
||||||
|
|
||||||
With two SQL queries, the number of joins could be reduced to the
|
With two SQL queries, the number of joins could be reduced to the
|
||||||
number of actuallly occurring subclasses in the result. A final
|
number of actuallly occurring subclasses in the specific result.
|
||||||
implementation might want to use one query only if the number of
|
|
||||||
possible subclasses (and therefore joins) is not too large, and
|
A perfect implementation might want to use one query only
|
||||||
two queries otherwise (using the first query to determine the
|
if the number of possible subclasses (and therefore joins) is not
|
||||||
actually occurring subclasses, reducing the number of joins for
|
too large, and two queries otherwise (using the first query to
|
||||||
the second).
|
determine the actually occurring subclasses, reducing the number
|
||||||
|
of joins for the second).
|
||||||
|
|
||||||
The number of joins needed for polymorphic object retrieval might
|
The number of joins needed for polymorphic object retrieval might
|
||||||
raise concerns regarding the efficiency of these database
|
raise concerns regarding the efficiency of these database
|
||||||
|
|
@ -455,18 +544,10 @@ 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,
|
be problematic, it is possible to split any problematic query into, for example,
|
||||||
two queries with only half the number of joins each.
|
two queries with only half the number of joins each.
|
||||||
|
|
||||||
In General
|
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.
|
||||||
Let's not forget that the above is just about optimization.
|
Such an optimization can be done at any later time (like when it's needed).
|
||||||
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).
|
|
||||||
|
|
||||||
|
|
||||||
.. _restrictions:
|
.. _restrictions:
|
||||||
|
|
@ -474,10 +555,12 @@ So it seems this optimization can be done at any later time
|
||||||
Restrictions & Caveats
|
Restrictions & Caveats
|
||||||
======================
|
======================
|
||||||
|
|
||||||
* The queryset methods ``values()``, ``values_list()``, ``select_related()``,
|
* Queryset methods ``values()``, ``values_list()``, ``select_related()``,
|
||||||
``defer()`` and ``only()`` are not yet fully supported (see above)
|
``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.
|
but it would most likely make sense to have one.
|
||||||
|
|
||||||
* Diamond shaped inheritance: There seems to be a general problem
|
* 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
|
by subclassing it instead of modifying Django core (as we do here
|
||||||
with PolymorphicModel).
|
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 the methods of the polymorphic querysets. Please see above
|
||||||
for ``translate_polymorphic_Q_object``.
|
for ``translate_polymorphic_Q_object``.
|
||||||
|
|
||||||
|
|
@ -501,7 +584,7 @@ Restrictions & Caveats
|
||||||
table needs to be corrected/copied too. This is of course generally
|
table needs to be corrected/copied too. This is of course generally
|
||||||
the case for any models using Django's ContentType.
|
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.
|
in the whole project, even if they are in two different apps.
|
||||||
This results from a restriction in the Django 1.1 "related_name"
|
This results from a restriction in the Django 1.1 "related_name"
|
||||||
option (fixed in Django 1.2).
|
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
|
+ http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Project Status
|
Project Status
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Django_polymorphic works well for a considerable number of users now,
|
Django_polymorphic works well for a considerable number of users now,
|
||||||
and no major problems have shown up for many months.
|
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
|
Links
|
||||||
|
|
|
||||||
100
README.html
100
README.html
|
|
@ -7,13 +7,22 @@
|
||||||
<title></title>
|
<title></title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
h1, h2, h3, h4 {
|
h1, h2, h3, h4,
|
||||||
|
#table-of-contents
|
||||||
|
{
|
||||||
color: #47c;
|
color: #47c;
|
||||||
}
|
}
|
||||||
|
h1 { padding-top: 15px; }
|
||||||
|
h2 { padding-top: 10px; }
|
||||||
|
h3 { padding-top: 7px; }
|
||||||
|
|
||||||
a:hover { border-bottom: 1px solid #0066cc; }
|
a:hover { border-bottom: 1px solid #0066cc; }
|
||||||
a {color: #0066cc; text-decoration: none;}
|
a {color: #0066cc; text-decoration: none;}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
tt {
|
tt {
|
||||||
color: #080;
|
color: #080;
|
||||||
|
|
@ -245,15 +254,15 @@ from the model <tt class="docutils literal">Project</tt>, and let's store one of
|
||||||
</pre>
|
</pre>
|
||||||
<p>Using django_polymorphic, we simply get what we stored:</p>
|
<p>Using django_polymorphic, we simply get what we stored:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
[ <Project: id 1, topic "John's Gathering">,
|
||||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
</pre>
|
</pre>
|
||||||
<p>Using vanilla Django, we get incomplete objects, which is probably not what we wanted:</p>
|
<p>Using vanilla Django, we get incomplete objects, which is probably not what we wanted:</p>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
[ <Project: id 1, topic "John's Gathering">,
|
||||||
<Project: id 2, topic: "Sculpting with Tim">,
|
<Project: id 2, topic "Painting with Tim">,
|
||||||
<Project: id 3, topic: "Swallow Aerodynamics"> ]
|
<Project: id 3, topic "Swallow Aerodynamics"> ]
|
||||||
</pre>
|
</pre>
|
||||||
<p>It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.</p>
|
<p>It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.</p>
|
||||||
<p>In general, the effect of django_polymorphic is twofold:</p>
|
<p>In general, the effect of django_polymorphic is twofold:</p>
|
||||||
|
|
@ -261,33 +270,26 @@ from the model <tt class="docutils literal">Project</tt>, and let's store one of
|
||||||
as you expect, by simply ensuring that you always get back exactly the same
|
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.
|
objects from the database you stored there - regardless how you access them.
|
||||||
This can save you a lot of unpleasant workarounds.</p>
|
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
|
django_polymorphic enables a much more expressive and intuitive
|
||||||
programming style and also very advanced object oriented
|
programming style and also very advanced object oriented
|
||||||
designs that are not possible with vanilla Django.</p>
|
designs that are not possible with vanilla Django.</p>
|
||||||
<p>Fortunately, most of the heavy duty machinery that is needed for this
|
<p>Fortunately, most of the heavy duty machinery that is needed for this
|
||||||
functionality is already present in the original Django database layer.
|
functionality is already present in the original Django database layer.
|
||||||
Django_polymorphic merely adds a rather thin layer above that, which is
|
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,
|
all that is required to make real OO fully automatic and very easy to use.</p>
|
||||||
with only minimal additions to Django's API.</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
|
<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>
|
<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>
|
||||||
<div class="section" id="this-is-a-v1-0-beta-testing-release">
|
<div class="section" id="this-is-a-v1-0-beta-testing-release">
|
||||||
<h2>This is a V1.0 Beta/Testing Release</h2>
|
<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
|
<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
|
critical parts of the software. It's intended for testing and development
|
||||||
environments and not for production environments. For these, it's best to
|
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
|
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 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>
|
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>
|
</div>
|
||||||
|
|
@ -295,15 +297,18 @@ or open an issue on <a class="reference external" href="http://github.com/bconst
|
||||||
<h1>License</h1>
|
<h1>License</h1>
|
||||||
<p>Django_polymorphic uses the same license as Django (BSD-like).</p>
|
<p>Django_polymorphic uses the same license as Django (BSD-like).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="api-changes">
|
<div class="section" id="api-changes-additions">
|
||||||
<h1>API Changes</h1>
|
<h1>API Changes & Additions</h1>
|
||||||
<div class="section" id="october-18-2010">
|
<div class="section" id="october-26-2010-v1-0-api-changes">
|
||||||
<h2>October 18 2010</h2>
|
<h2>October 26 2010, V1.0 API Changes</h2>
|
||||||
<div class="section" id="polymorphic-dumpdata">
|
<div class="section" id="extra-queryset-method">
|
||||||
<h3>polymorphic_dumpdata</h3>
|
<h3>extra() queryset method</h3>
|
||||||
<p>The polymorphic_dumpdata management command is not needed anymore
|
<p><tt class="docutils literal">.extra()</tt> has been re-implemented. Now it's polymorphic by
|
||||||
and has been removed, as the regular Django dumpdata command now automatically
|
default and works (nearly) without restrictions (please see docs). This is an
|
||||||
works correctly with polymorphic models (for all supported versions of Django).</p>
|
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>
|
||||||
<div class="section" id="output-of-queryset-or-object-printing">
|
<div class="section" id="output-of-queryset-or-object-printing">
|
||||||
<h3>Output of Queryset or Object Printing</h3>
|
<h3>Output of Queryset or Object Printing</h3>
|
||||||
|
|
@ -325,12 +330,45 @@ To get the old behaviour when printing querysets, you need to replace your model
|
||||||
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
|
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
|
||||||
<p>(the old ones still exist for compatibility)</p>
|
<p>(the old ones still exist for compatibility)</p>
|
||||||
</div>
|
</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">
|
<div class="section" id="running-the-test-suite-with-django-1-3">
|
||||||
<h3>Running the Test suite with Django 1.3</h3>
|
<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
|
<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>
|
just <tt class="docutils literal">python manage.py test</tt>.</p>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
||||||
|
>>> qs = ModelA.objects.all().non_polymorphic()
|
||||||
|
>>> real_objects = qs.get_real_instances()
|
||||||
|
</pre>
|
||||||
|
<p>is equivalent to:</p>
|
||||||
|
<pre class="doctest-block">
|
||||||
|
>>> 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">
|
<div class="section" id="february-22-2010-installation-note">
|
||||||
<h2>February 22 2010, Installation Note</h2>
|
<h2>February 22 2010, Installation Note</h2>
|
||||||
<p>The django_polymorphic source code has been restructured
|
<p>The django_polymorphic source code has been restructured
|
||||||
|
|
@ -354,10 +392,8 @@ from polymorphic.models import PolymorphicModel, ...
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="january-26-2010-database-schema-change">
|
<div class="section" id="january-26-2010-database-schema-change">
|
||||||
<h2>January 26 2010: Database Schema Change</h2>
|
<h2>January 26 2010: Database Schema Change</h2>
|
||||||
<div class="line-block">
|
<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>).
|
||||||
<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>
|
Sorry for any inconvenience. But this should be the final DB schema now.</p>
|
||||||
<div class="line">Sorry for any inconvenience. But this should be the final DB schema now.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
135
README.rst
135
README.rst
|
|
@ -29,14 +29,6 @@ Quick Start, Docs, Contributing
|
||||||
What is django_polymorphic good for?
|
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
|
Let's assume the models ``ArtProject`` and ``ResearchProject`` are derived
|
||||||
from the model ``Project``, and let's store one of each into the database:
|
from the model ``Project``, and let's store one of each into the database:
|
||||||
|
|
||||||
|
|
@ -50,38 +42,39 @@ If we want to retrieve all our projects, we do:
|
||||||
|
|
||||||
Using django_polymorphic, we simply get what we stored::
|
Using django_polymorphic, we simply get what we stored::
|
||||||
|
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
[ <Project: id 1, topic "Weekly Office Meeting">,
|
||||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
|
|
||||||
Using vanilla Django, we get incomplete objects, which is probably not what we wanted::
|
Using vanilla Django, we get incomplete objects, which is probably not what we wanted::
|
||||||
|
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
[ <Project: id 1, topic "Weekly Office Meeting">,
|
||||||
<Project: id 2, topic: "Sculpting with Tim">,
|
<Project: id 2, topic "Painting with Tim">,
|
||||||
<Project: id 3, topic: "Swallow Aerodynamics"> ]
|
<Project: id 3, topic "Swallow Aerodynamics"> ]
|
||||||
|
|
||||||
It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.
|
It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.
|
||||||
|
|
||||||
In general, the effect of django_polymorphic is twofold:
|
In general, the effect of django_polymorphic is twofold:
|
||||||
|
|
||||||
On one hand it makes sure that model inheritance just works
|
On one hand it makes sure that model inheritance just works as you
|
||||||
as you expect, by simply ensuring that you always get back exactly the same
|
expect, by simply ensuring that you always get back exactly thesame
|
||||||
objects from the database you stored there - regardless how you access them.
|
objects from the database you stored there - regardless how you access
|
||||||
This can save you a lot of unpleasant workarounds.
|
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,
|
On the other hand, together with some small API additions to the Django
|
||||||
django_polymorphic enables a much more expressive and intuitive
|
ORM, django_polymorphic enables a much more expressive and intuitive
|
||||||
programming style and also very advanced object oriented
|
programming style and also very advanced object oriented designs
|
||||||
designs that are not possible with vanilla Django.
|
that are not possible with vanilla Django.
|
||||||
|
|
||||||
Fortunately, most of the heavy duty machinery that is needed for this
|
Fortunately, most of the heavy duty machinery that is needed for this
|
||||||
functionality is already present in the original Django database layer.
|
functionality is already present in the original Django database layer.
|
||||||
Django_polymorphic merely adds a rather thin layer above that, which is
|
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,
|
all that is required to make real OO fully automatic and very easy to use.
|
||||||
with only minimal additions to Django's API.
|
|
||||||
|
|
||||||
For more information, please look at `Quickstart`_ or the complete
|
For more information, please look at `Quickstart`_ or at the complete
|
||||||
`Installation and Usage Docs`_. Please also see the `restrictions and caveats`_.
|
`Installation and Usage Docs`_.Please also see the `restrictions and caveats`_.
|
||||||
|
|
||||||
.. _restrictions and caveats: http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions
|
.. _restrictions and caveats: http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions
|
||||||
|
|
||||||
|
|
@ -89,23 +82,14 @@ For more information, please look at `Quickstart`_ or the complete
|
||||||
This is a V1.0 Beta/Testing Release
|
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
|
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
|
critical parts of the software. It's intended for testing and development
|
||||||
environments and not for production environments. For these, it's best to
|
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
|
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 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).
|
or open an issue on GitHub_ or BitBucket_ (or send me an email).
|
||||||
|
|
||||||
.. _discussion group: http://groups.google.de/group/django-polymorphic/topics
|
.. _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).
|
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
|
``.extra()`` has been re-implemented. Now it's polymorphic by
|
||||||
and has been removed, as the regular Django dumpdata command now automatically
|
default and works (nearly) without restrictions (please see docs). This is an
|
||||||
works correctly with polymorphic models (for all supported versions of Django).
|
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
|
In order to improve compatibility with vanilla Django, printing quersets does not use
|
||||||
django_polymorphic's pretty printing by default anymore.
|
django_polymorphic's pretty printing by default anymore.
|
||||||
|
|
@ -153,6 +141,23 @@ are now:
|
||||||
|
|
||||||
(the old ones still exist for compatibility)
|
(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
|
Running the Test suite with Django 1.3
|
||||||
++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
|
@ -160,8 +165,34 @@ Django 1.3 requires ``python manage.py test polymorphic`` instead of
|
||||||
just ``python manage.py test``.
|
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
|
February 22 2010, Installation Note
|
||||||
-----------------------------------
|
-------------------------------------------------------------------
|
||||||
|
|
||||||
The django_polymorphic source code has been restructured
|
The django_polymorphic source code has been restructured
|
||||||
and as a result needs to be installed like a normal Django App
|
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
|
January 26 2010: Database Schema Change
|
||||||
-----------------------------------------
|
-------------------------------------------------------------------
|
||||||
|
|
||||||
| The update from January 26 changed the database schema (more info in the commit-log_).
|
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.
|
Sorry for any inconvenience. But this should be the final DB schema now.
|
||||||
|
|
||||||
.. _commit-log: http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7
|
.. _commit-log: http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,11 @@ class Command(NoArgsCommand):
|
||||||
a=ModelA.objects.create(field1='A1')
|
a=ModelA.objects.create(field1='A1')
|
||||||
b=ModelB.objects.create(field1='B1', field2='B2')
|
b=ModelB.objects.create(field1='B1', field2='B2')
|
||||||
c=ModelC.objects.create(field1='C1', field2='C2', field3='C3')
|
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 = 'A0'"} )
|
||||||
|
=======
|
||||||
|
print ModelA.objects.extra( select={"select1": "field1 = 'A1'", "select2": "field1 != 'A1'"} )
|
||||||
|
>>>>>>> 7c2be35... pexp:pexp/management/commands/p2cmd.py
|
||||||
print
|
print
|
||||||
|
|
||||||
if not 'UUIDField' in globals(): return
|
if not 'UUIDField' in globals(): return
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import settings
|
||||||
import sys
|
import sys
|
||||||
from pexp.models import *
|
from pexp.models import *
|
||||||
|
|
||||||
num_objects=15000
|
num_objects=1000
|
||||||
|
|
||||||
def reset_queries():
|
def reset_queries():
|
||||||
connection.queries=[]
|
connection.queries=[]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from django.db import models
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
||||||
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
||||||
|
|
||||||
|
|
||||||
class Project(ShowFieldContent, PolymorphicModel):
|
class Project(ShowFieldContent, PolymorphicModel):
|
||||||
topic = models.CharField(max_length=30)
|
topic = models.CharField(max_length=30)
|
||||||
class ArtProject(Project):
|
class ArtProject(Project):
|
||||||
|
|
@ -27,7 +26,6 @@ class nModelB(nModelA):
|
||||||
class nModelC(nModelB):
|
class nModelC(nModelB):
|
||||||
field3 = models.CharField(max_length=10)
|
field3 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
|
||||||
# for Django 1.2+, test models with same names in different apps
|
# for Django 1.2+, test models with same names in different apps
|
||||||
# (the other models with identical names are in polymorphic/tests.py)
|
# (the other models with identical names are in polymorphic/tests.py)
|
||||||
from django import VERSION as django_VERSION
|
from django import VERSION as django_VERSION
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,10 @@ class PolymorphicModel(models.Model):
|
||||||
and provides a polymorphic manager as the default manager
|
and provides a polymorphic manager as the default manager
|
||||||
(and as 'objects').
|
(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
|
If your derived class overrides any of these methods as well, then you need
|
||||||
to take care that you correctly call the save() method of
|
to take care that you correctly call the method of the superclass, like:
|
||||||
the superclass, like:
|
|
||||||
|
|
||||||
super(YourClass,self).save(*args,**kwargs)
|
super(YourClass,self).save(*args,**kwargs)
|
||||||
"""
|
"""
|
||||||
|
|
@ -116,7 +115,7 @@ class PolymorphicModel(models.Model):
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, * args, ** kwargs):
|
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.
|
(self.__class__) with our own versions.
|
||||||
We monkey patch them until a patch can be added to Django
|
We monkey patch them until a patch can be added to Django
|
||||||
(which would probably be very small and make all of this obsolete).
|
(which would probably be very small and make all of this obsolete).
|
||||||
|
|
@ -145,7 +144,7 @@ class PolymorphicModel(models.Model):
|
||||||
return attr
|
return attr
|
||||||
return accessor_function
|
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
|
from django.db.models.fields.related import SingleRelatedObjectDescriptor, ReverseSingleRelatedObjectDescriptor
|
||||||
for name,model in subclasses_and_superclasses_accessors.iteritems():
|
for name,model in subclasses_and_superclasses_accessors.iteritems():
|
||||||
|
|
@ -154,7 +153,7 @@ class PolymorphicModel(models.Model):
|
||||||
#print >>sys.stderr, '---------- replacing',name, orig_accessor
|
#print >>sys.stderr, '---------- replacing',name, orig_accessor
|
||||||
setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)) )
|
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__:
|
"""helper function for __init__:
|
||||||
determine names of all Django inheritance accessor member functions for type(self)"""
|
determine names of all Django inheritance accessor member functions for type(self)"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,8 @@ __test__ = {"doctest": """
|
||||||
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
|
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
|
||||||
|
|
||||||
# non_polymorphic()
|
# non_polymorphic()
|
||||||
>>> Model2A.objects.all().non_polymorphic()
|
>>> qs=Model2A.objects.all().non_polymorphic()
|
||||||
|
>>> qs
|
||||||
[ <Model2A: id 1, field1 (CharField)>,
|
[ <Model2A: id 1, field1 (CharField)>,
|
||||||
<Model2A: id 2, field1 (CharField)>,
|
<Model2A: id 2, field1 (CharField)>,
|
||||||
<Model2A: id 3, field1 (CharField)>,
|
<Model2A: id 3, field1 (CharField)>,
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,22 @@
|
||||||
|
|
||||||
import sys,os
|
import sys,os
|
||||||
|
|
||||||
|
dopart = None
|
||||||
|
if len(sys.argv)>1: dopart = sys.argv[1]
|
||||||
|
noshow = 'noshow' in sys.argv
|
||||||
|
|
||||||
css='--stylesheet-path=rst.css'
|
css='--stylesheet-path=rst.css'
|
||||||
|
|
||||||
def conv(name):
|
def conv(name):
|
||||||
if len(sys.argv)>1:
|
print 'convert',name
|
||||||
|
if noshow:
|
||||||
os.system('rst2html.py '+css+' %s.rst >%s.html' % (name, name) )
|
os.system('rst2html.py '+css+' %s.rst >%s.html' % (name, name) )
|
||||||
else:
|
else:
|
||||||
os.system('rst2html.py '+css+' %s.rst >%s.html ; firefox %s.html' % (name, name, name) )
|
os.system('rst2html.py '+css+' %s.rst >%s.html ; firefox %s.html' % (name, name, name) )
|
||||||
|
|
||||||
conv('DOCS')
|
if not dopart or dopart=='1': conv('DOCS')
|
||||||
conv('README')
|
if not dopart or dopart=='2': conv('README')
|
||||||
conv('CHANGES')
|
if not dopart or dopart=='3': conv('CHANGES')
|
||||||
|
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
|
||||||
11
rst.css
11
rst.css
|
|
@ -1,10 +1,19 @@
|
||||||
h1, h2, h3, h4 {
|
h1, h2, h3, h4,
|
||||||
|
#table-of-contents
|
||||||
|
{
|
||||||
color: #47c;
|
color: #47c;
|
||||||
}
|
}
|
||||||
|
h1 { padding-top: 15px; }
|
||||||
|
h2 { padding-top: 10px; }
|
||||||
|
h3 { padding-top: 7px; }
|
||||||
|
|
||||||
a:hover { border-bottom: 1px solid #0066cc; }
|
a:hover { border-bottom: 1px solid #0066cc; }
|
||||||
a {color: #0066cc; text-decoration: none;}
|
a {color: #0066cc; text-decoration: none;}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
tt {
|
tt {
|
||||||
color: #080;
|
color: #080;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue