Documentation updated.

Test_all_versions script added.
Some minor misc changes.
Added polybench.
fix_request_path_info
Bert Constantin 2010-10-20 09:31:36 +02:00
parent c10ff1650b
commit b1905026bc
20 changed files with 2740 additions and 299 deletions

2
.gitignore vendored
View File

@ -23,6 +23,4 @@ ppreadme.py
ppdocs.py
common.css
screen.css
README.html
DOCS.html

438
CHANGES.html 100644
View File

@ -0,0 +1,438 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title></title>
<style type="text/css">
h1, h2, h3, h4 {
color: #47c;
}
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
tt {
color: #080;
}
blockquote tt {
color: #000
}
.first {
margin-top: 0 }
.last {
margin-bottom: 0 }
/*
a.toc-backref {
text-decoration: none ;
color: black }
*/
dd {
margin-bottom: 0.5em }
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em }
div.footer, div.header {
font-size: smaller }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr {
width: 75% }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.line-block {
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option-argument {
font-style: italic }
span.pre {
white-space: pre }
span.problematic {
color: red }
table {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.citation {
border-left: solid thin gray ;
padding-left: 0.5ex }
table.docinfo {
margin: 2em 4em }
table.footnote {
border-left: solid thin black ;
padding-left: 0.5ex }
td, th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
th.docinfo-name, th.field-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap }
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100% }
tt, pre.literal-block, pre.doctest-block {
font-size: 115%;
line-height: 150% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<div class="section" id="django-polymorphic">
<h1><em>django_polymorphic</em></h1>
</div>
<div class="section" id="changelog">
<h1>Changelog</h1>
<p>.</p>
<hr class="docutils" />
<div class="section" id="id1">
<h2>2010-10-18</h2>
<div class="section" id="this-is-a-v1-0-beta-testing-release">
<h3>This is a V1.0 Beta/Testing Release</h3>
<p>This release is mostly a cleanup and maintenance release that also
improves a number of minor things and fixes one (non-critical) bug.</p>
<p>Some pending API changes and corrections have been folded into this release
in order to make the upcoming V1.0 API as stable as possible.</p>
<p>This release is also about getting feedback from you in case you don't
approve of any of these changes or would like to get additional
API fixes into V1.0.</p>
<p>The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
potential problems to turn up (if they exist).</p>
<p>If you encounter any such problems, please post them in the discussion group
or open an issue on GitHub or BitBucket (or send me an email).</p>
<p>There also have been a number of minor API changes.
Please see the README for more information.</p>
</div>
<div class="section" id="new-features">
<h3>New Features</h3>
<ul>
<li><p class="first">official Django 1.3 alpha compatibility</p>
</li>
<li><p class="first"><tt class="docutils literal">PolymorphicModel.__getattribute__</tt> hack removed.
The python __getattribute__ hack generally causes a considerable
overhead and to have this in the performance-sensitive PolymorphicModel
class was somewhat problematic. It's gone for good now.</p>
</li>
<li><p class="first"><tt class="docutils literal">polymorphic_dumpdata</tt> management command functionality removed:
The regular Django dumpdata command now automatically works correctly
for polymorphic models with all Django versions.</p>
</li>
<li><p class="first">.get_real_instances() has been elevated to an official part of the API:</p>
<pre class="literal-block">
real_objects = ModelA.objects.get_real_instances(base_objects_list_or_queryset)
</pre>
<p>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.base_objects.extra(...)</span></tt> and then want to
transform the result to its polymorphic equivalent.</p>
</li>
<li><p class="first"><tt class="docutils literal">translate_polymorphic_Q_object</tt> (see DOCS)</p>
</li>
<li><p class="first">improved testing</p>
</li>
<li><p class="first">Changelog added: CHANGES.rst/html</p>
</li>
</ul>
</div>
<div class="section" id="bugfixes">
<h3>Bugfixes</h3>
<ul class="simple">
<li>removed requirement for primary key to be an IntegerField.
Thanks to Mathieu Steele and Malthe Borch.</li>
</ul>
</div>
<div class="section" id="api-changes">
<h3>API Changes</h3>
<div class="section" id="polymorphic-dumpdata">
<h4>polymorphic_dumpdata</h4>
<p>The polymorphic_dumpdata management command is not needed anymore
and has been removed, as the regular Django dumpdata command now automatically
works correctly with polymorphic models (for all supported versions of Django).</p>
</div>
<div class="section" id="output-of-queryset-or-object-printing">
<h4>Output of Queryset or Object Printing</h4>
<p>In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
To get the old behaviour when printing querysets, you need to replace your model definition:</p>
<pre class="doctest-block">
&gt;&gt;&gt; class Project(PolymorphicModel):
</pre>
<p>by:</p>
<pre class="doctest-block">
&gt;&gt;&gt; class Project(PolymorphicModel, ShowFieldType):
</pre>
<p>The mixin classes for pretty output have been renamed:</p>
<blockquote>
<tt class="docutils literal">ShowFieldTypes, ShowFields, ShowFieldsAndTypes</tt></blockquote>
<p>are now:</p>
<blockquote>
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
<p>(the old ones still exist for compatibility)</p>
</div>
<div class="section" id="running-the-test-suite-with-django-1-3">
<h4>Running the Test suite with Django 1.3</h4>
<p>Django 1.3 requires <tt class="docutils literal">python manage.py test polymorphic</tt> instead of
just <tt class="docutils literal">python manage.py test</tt>.</p>
</div>
</div>
</div>
<hr class="docutils" />
<div class="section" id="id2">
<h2>2010-2-22</h2>
<p>IMPORTANT: API Changed (import path changed), and Installation Note</p>
<p>The django_polymorphic source code has been restructured
and as a result needs to be installed like a normal Django App
- either via copying the &quot;polymorphic&quot; directory into your
Django project or by running setup.py. Adding 'polymorphic'
to INSTALLED_APPS in settings.py is still optional, however.</p>
<p>The file <cite>polymorphic.py</cite> cannot be used as a standalone
extension module anymore, as is has been split into a number
of smaller files.</p>
<p>Importing works slightly different now: All relevant symbols are
imported directly from 'polymorphic' instead from
'polymorphic.models':</p>
<pre class="literal-block">
# new way
from polymorphic import PolymorphicModel, ...
# old way, doesn't work anymore
from polymorphic.models import PolymorphicModel, ...
</pre>
<ul class="simple">
<li>minor API addition: 'from polymorphic import VERSION, get_version'</li>
</ul>
<div class="section" id="id3">
<h3>New Features</h3>
<p>Python 2.4 compatibility, contributed by Charles Leifer. Thanks!</p>
</div>
<div class="section" id="id4">
<h3>Bugfixes</h3>
<p>Fix: The exception &quot;...has no attribute 'sub_and_superclass_dict'&quot;
could be raised. (This occurred if a subclass defined __init__
and accessed class members before calling the superclass __init__).
Thanks to Mattias Brändström.</p>
<p>Fix: There could be name conflicts if
field_name == model_name.lower() or similar.
Now it is possible to give a field the same name as the class
(like with normal Django models).
(Found through the example provided by Mattias Brändström)</p>
</div>
</div>
<hr class="docutils" />
<div class="section" id="id5">
<h2>2010-2-4</h2>
<div class="section" id="new-features-and-documentation">
<h3>New features (and documentation)</h3>
<p>queryset order_by method added</p>
<p>queryset aggregate() and extra() methods implemented</p>
<p>queryset annotate() method implemented</p>
<p>queryset values(), values_list(), distinct() documented; defer(),
only() allowed (but not yet supported)</p>
<p>setup.py added. Thanks to Andrew Ingram.</p>
<p>More about these additions in the docs:
<a class="reference external" href="http://bserve.webhop.org/wiki/django_polymorphic/doc">http://bserve.webhop.org/wiki/django_polymorphic/doc</a></p>
</div>
<div class="section" id="id6">
<h3>Bugfixes</h3>
<ul class="simple">
<li>fix remaining potential accessor name clashes (but this only works
with Django 1.2+, for 1.1 no changes). Thanks to Andrew Ingram.</li>
<li>fix use of 'id' model field, replaced with 'pk'.</li>
<li>fix select_related bug for objects from derived classes (till now
sel.-r. was just ignored)</li>
</ul>
</div>
<div class="section" id="restrictions-caveats-updated">
<h3>&quot;Restrictions &amp; Caveats&quot; updated</h3>
<ul class="simple">
<li>Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 &quot;related_name&quot;
option (fixed in Django 1.2).</li>
<li>Django 1.1 only - when ContentType is used in models, Django's
seralisation or fixtures cannot be used. This issue seems to be
resolved for Django 1.2 (changeset 11863: Fixed #7052, Added
support for natural keys in serialization).</li>
</ul>
</div>
</div>
<hr class="docutils" />
<div class="section" id="id7">
<h2>2010-1-30</h2>
<p>Fixed ContentType related field accessor clash (an error emitted
by model validation) by adding related_name to the ContentType
ForeignKey. This happened if your polymorphc model used a ContentType
ForeignKey. Thanks to Andrew Ingram.</p>
</div>
<hr class="docutils" />
<div class="section" id="id8">
<h2>2010-1-29</h2>
<p>Restructured django_polymorphic into a regular Django add-on
application. This is needed for the management commands, and
also seems to be a generally good idea for future enhancements
as well (and it makes sure the tests are always included).</p>
<p>The <tt class="docutils literal">poly</tt> app - until now being used for test purposes only
- has been renamed to <tt class="docutils literal">polymorphic</tt>. See DOCS.rst
(&quot;installation/testing&quot;) for more info.</p>
</div>
<hr class="docutils" />
<div class="section" id="id9">
<h2>2010-1-28</h2>
<p>Added the polymorphic_dumpdata management command (github issue 4),
for creating fixtures, this should be used instead of
the normal Django dumpdata command.
Thanks to Charles Leifer.</p>
<p>Important: Using ContentType together with dumpdata generally
needs Django 1.2 (important as any polymorphic model uses
ContentType).</p>
</div>
<hr class="docutils" />
<div class="section" id="id10">
<h2>2010-1-26</h2>
<p>IMPORTANT - database schema change (more info in change log).
I hope I got this change in early enough before anyone started
to use polymorphic.py in earnest. Sorry for any inconvenience.
This should be the final DB schema now.</p>
<p>Django's ContentType is now used instead of app-label and model-name
This is a cleaner and more efficient solution
Thanks to Ilya Semenov for the suggestion.</p>
</div>
</div>
</div>
</body>
</html>

266
CHANGES.rst 100644
View File

@ -0,0 +1,266 @@
*django_polymorphic*
++++++++++++++++++++
Changelog
++++++++++
.
------------------------------------------------------------------
2010-10-18
==========
This is a V1.0 Beta/Testing Release
-----------------------------------
This release is mostly a cleanup and maintenance release that also
improves a number of minor things and fixes one (non-critical) bug.
Some pending API changes and corrections have been folded into this release
in order to make the upcoming V1.0 API as stable as possible.
This release is also about getting feedback from you in case you don't
approve of any of these changes or would like to get additional
API fixes into V1.0.
The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
potential problems to turn up (if they exist).
If you encounter any such problems, please post them in the discussion group
or open an issue on GitHub or BitBucket (or send me an email).
There also have been a number of minor API changes.
Please see the README for more information.
New Features
------------------------
* official Django 1.3 alpha compatibility
* ``PolymorphicModel.__getattribute__`` hack removed.
The python __getattribute__ hack generally causes a considerable
overhead and to have this in the performance-sensitive PolymorphicModel
class was somewhat problematic. It's gone for good now.
* ``polymorphic_dumpdata`` management command functionality removed:
The regular Django dumpdata command now automatically works correctly
for polymorphic models with all Django versions.
* .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)
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.base_objects.extra(...)`` and then want to
transform the result to its polymorphic equivalent.
* ``translate_polymorphic_Q_object`` (see DOCS)
* improved testing
* Changelog added: CHANGES.rst/html
Bugfixes
------------------------
* removed requirement for primary key to be an IntegerField.
Thanks to Mathieu Steele and Malthe Borch.
API Changes
-----------
polymorphic_dumpdata
####################
The polymorphic_dumpdata management command is not needed anymore
and has been removed, as the regular Django dumpdata command now automatically
works correctly with polymorphic models (for all supported versions of Django).
Output of Queryset or Object Printing
#####################################
In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
To get the old behaviour when printing querysets, you need to replace your model definition:
>>> class Project(PolymorphicModel):
by:
>>> class Project(PolymorphicModel, ShowFieldType):
The mixin classes for pretty output have been renamed:
``ShowFieldTypes, ShowFields, ShowFieldsAndTypes``
are now:
``ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent``
(the old ones still exist for compatibility)
Running the Test suite with Django 1.3
######################################
Django 1.3 requires ``python manage.py test polymorphic`` instead of
just ``python manage.py test``.
------------------------------------------------------------------
2010-2-22
==========
IMPORTANT: API Changed (import path changed), and Installation Note
The django_polymorphic source code has been restructured
and as a result needs to be installed like a normal Django App
- either via copying the "polymorphic" directory into your
Django project or by running setup.py. Adding 'polymorphic'
to INSTALLED_APPS in settings.py is still optional, however.
The file `polymorphic.py` cannot be used as a standalone
extension module anymore, as is has been split into a number
of smaller files.
Importing works slightly different now: All relevant symbols are
imported directly from 'polymorphic' instead from
'polymorphic.models'::
# new way
from polymorphic import PolymorphicModel, ...
# old way, doesn't work anymore
from polymorphic.models import PolymorphicModel, ...
+ minor API addition: 'from polymorphic import VERSION, get_version'
New Features
------------------------
Python 2.4 compatibility, contributed by Charles Leifer. Thanks!
Bugfixes
------------------------
Fix: The exception "...has no attribute 'sub_and_superclass_dict'"
could be raised. (This occurred if a subclass defined __init__
and accessed class members before calling the superclass __init__).
Thanks to Mattias Brändström.
Fix: There could be name conflicts if
field_name == model_name.lower() or similar.
Now it is possible to give a field the same name as the class
(like with normal Django models).
(Found through the example provided by Mattias Brändström)
------------------------------------------------------------------
2010-2-4
==========
New features (and documentation)
-----------------------------------------
queryset order_by method added
queryset aggregate() and extra() methods implemented
queryset annotate() method implemented
queryset values(), values_list(), distinct() documented; defer(),
only() allowed (but not yet supported)
setup.py added. Thanks to Andrew Ingram.
More about these additions in the docs:
http://bserve.webhop.org/wiki/django_polymorphic/doc
Bugfixes
------------------------
* fix remaining potential accessor name clashes (but this only works
with Django 1.2+, for 1.1 no changes). Thanks to Andrew Ingram.
* fix use of 'id' model field, replaced with 'pk'.
* fix select_related bug for objects from derived classes (till now
sel.-r. was just ignored)
"Restrictions & Caveats" updated
----------------------------------------
* Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 "related_name"
option (fixed in Django 1.2).
* Django 1.1 only - when ContentType is used in models, Django's
seralisation or fixtures cannot be used. This issue seems to be
resolved for Django 1.2 (changeset 11863: Fixed #7052, Added
support for natural keys in serialization).
------------------------------------------------------------------
2010-1-30
==========
Fixed ContentType related field accessor clash (an error emitted
by model validation) by adding related_name to the ContentType
ForeignKey. This happened if your polymorphc model used a ContentType
ForeignKey. Thanks to Andrew Ingram.
------------------------------------------------------------------
2010-1-29
==========
Restructured django_polymorphic into a regular Django add-on
application. This is needed for the management commands, and
also seems to be a generally good idea for future enhancements
as well (and it makes sure the tests are always included).
The ``poly`` app - until now being used for test purposes only
- has been renamed to ``polymorphic``. See DOCS.rst
("installation/testing") for more info.
------------------------------------------------------------------
2010-1-28
==========
Added the polymorphic_dumpdata management command (github issue 4),
for creating fixtures, this should be used instead of
the normal Django dumpdata command.
Thanks to Charles Leifer.
Important: Using ContentType together with dumpdata generally
needs Django 1.2 (important as any polymorphic model uses
ContentType).
------------------------------------------------------------------
2010-1-26
==========
IMPORTANT - database schema change (more info in change log).
I hope I got this change in early enough before anyone started
to use polymorphic.py in earnest. Sorry for any inconvenience.
This should be the final DB schema now.
Django's ContentType is now used instead of app-label and model-name
This is a cleaner and more efficient solution
Thanks to Ilya Semenov for the suggestion.

753
DOCS.html 100644
View File

@ -0,0 +1,753 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title></title>
<style type="text/css">
h1, h2, h3, h4 {
color: #47c;
}
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
tt {
color: #080;
}
blockquote tt {
color: #000
}
.first {
margin-top: 0 }
.last {
margin-bottom: 0 }
/*
a.toc-backref {
text-decoration: none ;
color: black }
*/
dd {
margin-bottom: 0.5em }
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em }
div.footer, div.header {
font-size: smaller }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr {
width: 75% }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.line-block {
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option-argument {
font-style: italic }
span.pre {
white-space: pre }
span.problematic {
color: red }
table {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.citation {
border-left: solid thin gray ;
padding-left: 0.5ex }
table.docinfo {
margin: 2em 4em }
table.footnote {
border-left: solid thin black ;
padding-left: 0.5ex }
td, th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
th.docinfo-name, th.field-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap }
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100% }
tt, pre.literal-block, pre.doctest-block {
font-size: 115%;
line-height: 150% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<div class="section" id="polymorphic-models-for-django">
<h1><a class="toc-backref" href="#id1">Polymorphic Models for Django</a></h1>
<div class="contents topic" id="table-of-contents">
<p class="topic-title first">Table of Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#polymorphic-models-for-django" id="id1">Polymorphic Models for Django</a></li>
<li><a class="reference internal" href="#quickstart" id="id2">Quickstart</a></li>
<li><a class="reference internal" href="#more-about-installation-testing" id="id3">More about Installation / Testing</a></li>
<li><a class="reference internal" href="#more-polymorphic-functionality" id="id4">More Polymorphic Functionality</a></li>
<li><a class="reference internal" href="#custom-managers-querysets-manager-inheritance" id="id5">Custom Managers, Querysets &amp; Manager Inheritance</a></li>
<li><a class="reference internal" href="#performance-considerations" id="id6">Performance Considerations</a></li>
<li><a class="reference internal" href="#possible-optimizations" id="id7">Possible Optimizations</a></li>
<li><a class="reference internal" href="#restrictions-caveats" id="id8">Restrictions &amp; Caveats</a></li>
<li><a class="reference internal" href="#project-status" id="id9">Project Status</a></li>
<li><a class="reference internal" href="#links" id="id10">Links</a></li>
</ul>
</div>
</div>
<div class="section" id="quickstart">
<h1><a class="toc-backref" href="#id2">Quickstart</a></h1>
<div class="section" id="install">
<h2>Install</h2>
<p>After uncompressing (if necessary), in the directory &quot;django_polymorphic&quot;,
execute (on Unix-like systems):</p>
<pre class="literal-block">
sudo python setup.py install
</pre>
</div>
<div class="section" id="make-your-models-polymorphic">
<h2>Make Your Models Polymorphic</h2>
<p>Use <tt class="docutils literal">PolymorphicModel</tt> instead of Django's <tt class="docutils literal">models.Model</tt>, like so:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel
class Project(PolymorphicModel):
topic = models.CharField(max_length=30)
class ArtProject(Project):
artist = models.CharField(max_length=30)
class ResearchProject(Project):
supervisor = models.CharField(max_length=30)
</pre>
<p>All models inheriting from your polymorphic models will be polymorphic as well.</p>
</div>
<div class="section" id="create-some-objects">
<h2>Create some objects</h2>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.create(topic=&quot;John's Gathering&quot;)
&gt;&gt;&gt; ArtProject.objects.create(topic=&quot;Sculpting with Tim&quot;, artist=&quot;T. Turner&quot;)
&gt;&gt;&gt; ResearchProject.objects.create(topic=&quot;Swallow Aerodynamics&quot;, supervisor=&quot;Dr. Winter&quot;)
</pre>
</div>
<div class="section" id="get-polymorphic-query-results">
<h2>Get polymorphic query results</h2>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.all()
[ &lt;Project: id 1, topic: &quot;John's Gathering&quot;&gt;,
&lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
&lt;ResearchProject: id 3, topic: &quot;Swallow Aerodynamics&quot;, supervisor: &quot;Dr. Winter&quot;&gt; ]
</pre>
<p>using instance_of and not_instance_of for narrowing the result to specific subtypes:</p>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.instance_of(ArtProject)
[ &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt; ]
</pre>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
[ &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
&lt;ResearchProject: id 3, topic: &quot;Swallow Aerodynamics&quot;, supervisor: &quot;Dr. Winter&quot;&gt; ]
</pre>
<p>Polymorphic filtering: Let's get all projects where Mr. Turner is involved as an artist
or supervisor (note the three underscores):</p>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
[ &lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
&lt;ResearchProject: id 3, topic: &quot;History of Sculpting&quot;, supervisor: &quot;T. Turner&quot;&gt; ]
</pre>
</div>
<div class="section" id="what-s-more">
<h2>What's More?</h2>
<p>Most of Django's standard ORM functionality is available and works as expected.
ForeignKeys, ManyToManyFields and OneToToneFields to your polymorphic
models work as shey should (polymorphic).</p>
<p>In short, with django_polymorphic the Django models are much more &quot;pythonic&quot;, i.e.
they just work as you expect them to work: very similar to ordinary python classes
(which is not the case with vanilla Django model inheritance).</p>
<p>Note: In all example output, above and below, for a nicer and more informative
output the <cite>ShowFieldType</cite> mixin has been used (documented below).</p>
</div>
</div>
<div class="section" id="more-about-installation-testing">
<h1><a class="toc-backref" href="#id3">More about Installation / Testing</a></h1>
<div class="section" id="requirements">
<h2>Requirements</h2>
<p>Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
on Django 1.1.1 / 1.2 beta and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
</div>
<div class="section" id="included-test-suite">
<h2>Included Test Suite</h2>
<p>The repository (or tar file) contains a complete Django project
that may be used for tests or experiments, without any installation needed.</p>
<p>To run the included test suite, execute:</p>
<pre class="literal-block">
./manage test polymorphic
</pre>
<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used for
experiments - modify this file (pexp/management/commands/pcmd.py)
to your liking, then run:</p>
<pre class="literal-block">
./manage syncdb # db is created in /var/tmp/... (settings.py)
./manage pcmd
</pre>
</div>
<div class="section" id="installation">
<h2>Installation</h2>
<p>In the directory &quot;django_polymorphic&quot;, execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
<p>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> directory
(under &quot;django_polymorphic&quot;) into your Django project dir
(e.g. if you want to distribute your project with more 'batteries included').</p>
<p>If you want to use the management command <tt class="docutils literal">polymorphic_dumpdata</tt>, then
you need to add <tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting. This is also
needed if you want to run the test cases in <cite>polymorphic/tests.py</cite>.</p>
<p>In any case, Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
needs to be listed in INSTALLED_APPS (usually it already is).</p>
</div>
</div>
<div class="section" id="more-polymorphic-functionality">
<h1><a class="toc-backref" href="#id4">More Polymorphic Functionality</a></h1>
<p>In the examples below, these models are being used:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel
class ModelA(PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelB(ModelA):
field2 = models.CharField(max_length=10)
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
</pre>
<div class="section" id="filtering-for-classes-equivalent-to-python-s-isinstance">
<h2>Filtering for classes (equivalent to python's isinstance() ):</h2>
<pre class="doctest-block">
&gt;&gt;&gt; ModelA.objects.instance_of(ModelB)
.
[ &lt;ModelB: id 2, field1 (CharField), field2 (CharField)&gt;,
&lt;ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)&gt; ]
</pre>
<p>In general, including or excluding parts of the inheritance tree:</p>
<pre class="literal-block">
ModelA.objects.instance_of(ModelB [, ModelC ...])
ModelA.objects.not_instance_of(ModelB [, ModelC ...])
</pre>
<p>You can also use this feature in Q-objects (with the same result as above):</p>
<pre class="doctest-block">
&gt;&gt;&gt; ModelA.objects.filter( Q(instance_of=ModelB) )
</pre>
</div>
<div class="section" id="polymorphic-filtering-for-fields-in-derived-classes">
<h2>Polymorphic filtering (for fields in derived classes)</h2>
<p>For example, cherrypicking objects from multiple derived classes
anywhere in the inheritance tree, using Q objects (with the
syntax: <tt class="docutils literal">exact model name + three _ + field name</tt>):</p>
<pre class="doctest-block">
&gt;&gt;&gt; ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
.
[ &lt;ModelB: id 2, field1 (CharField), field2 (CharField)&gt;,
&lt;ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)&gt; ]
</pre>
</div>
<div class="section" id="combining-querysets-of-different-types-models">
<h2>Combining Querysets of different types/models</h2>
<p>Querysets may now be regarded as object containers that allow the
aggregation of different object types - very similar to python
lists (as long as the objects are accessed through the manager of
a common base class):</p>
<pre class="doctest-block">
&gt;&gt;&gt; Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
.
[ &lt;ModelX: id 1, field_x (CharField)&gt;,
&lt;ModelY: id 2, field_y (CharField)&gt; ]
</pre>
</div>
<div class="section" id="manytomanyfield-foreignkey-onetoonefield">
<h2>ManyToManyField, ForeignKey, OneToOneField</h2>
<p>Relationship fields referring to polymorphic models work as
expected: like polymorphic querysets they now always return the
referred objects with the same type/class these were created and
saved as.</p>
<p>E.g., if in your model you define:</p>
<pre class="literal-block">
field1 = OneToOneField(ModelA)
</pre>
<p>then field1 may now also refer to objects of type <tt class="docutils literal">ModelB</tt> or <tt class="docutils literal">ModelC</tt>.</p>
<p>A ManyToManyField example:</p>
<pre class="literal-block">
# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
many2many = models.ManyToManyField('ModelA') # ManyToMany relation to a polymorphic model
&gt;&gt;&gt; o=RelatingModel.objects.create()
&gt;&gt;&gt; o.many2many.add(ModelA.objects.get(id=1))
&gt;&gt;&gt; o.many2many.add(ModelB.objects.get(id=2))
&gt;&gt;&gt; o.many2many.add(ModelC.objects.get(id=3))
&gt;&gt;&gt; o.many2many.all()
[ &lt;ModelA: id 1, field1 (CharField)&gt;,
&lt;ModelB: id 2, field1 (CharField), field2 (CharField)&gt;,
&lt;ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)&gt; ]
</pre>
</div>
<div class="section" id="using-third-party-models-without-modifying-them">
<h2>Using Third Party Models (without modifying them)</h2>
<p>Third party models can be used as polymorphic models without
restrictions by subclassing them. E.g. using a third party
model as the root of a polymorphic inheritance tree:</p>
<pre class="literal-block">
from thirdparty import ThirdPartyModel
class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
</pre>
<p>Or instead integrating the third party model anywhere into an
existing polymorphic inheritance tree:</p>
<pre class="literal-block">
class MyModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)
class MyModelWithThirdParty(MyModel, ThirdPartyModel):
pass # or add fields
</pre>
</div>
<div class="section" id="non-polymorphic-queries">
<h2>Non-Polymorphic Queries</h2>
<pre class="doctest-block">
&gt;&gt;&gt; ModelA.base_objects.all()
.
[ &lt;ModelA: id 1, field1 (CharField)&gt;,
&lt;ModelA: id 2, field1 (CharField)&gt;,
&lt;ModelA: id 3, field1 (CharField)&gt; ]
</pre>
<p>Each polymorphic model has 'base_objects' defined as a normal
Django manager. Of course, arbitrary custom managers may be
added to the models as well.</p>
</div>
<div class="section" id="about-queryset-methods">
<h2>About Queryset Methods</h2>
<ul class="simple">
<li><tt class="docutils literal">annotate()</tt> and <tt class="docutils literal">aggregate()</tt> work just as usual, with the
addition that the <tt class="docutils literal">ModelX___field</tt> syntax can be used for the
keyword arguments (but not for the non-keyword arguments).</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">order_by()</tt> now similarly supports the <tt class="docutils literal">ModelX___field</tt> syntax
for specifying ordering through a field in a submodel.</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">distinct()</tt> works as expected. It only regards the fields of
the base class, but this should never make a difference.</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">select_related()</tt> works just as usual, but it can not (yet) be used
to select relations in derived models
(like <tt class="docutils literal"><span class="pre">ModelA.objects.select_related('ModelC___fieldxy')</span></tt> )</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">extra()</tt> by default works exactly like the original version,
with the resulting queryset not being polymorphic. There is
experimental support for a polymorphic extra() via the keyword
argument <tt class="docutils literal">polymorphic=True</tt> (only the <tt class="docutils literal">where</tt> and
<tt class="docutils literal">order_by</tt> and <tt class="docutils literal">params</tt> arguments of extra() should be used then).
The behaviour of extra() may change in the future, so it's best if you use
<tt class="docutils literal"><span class="pre">base_objects=ModelA.base_objects.extra(...)</span></tt> instead if you want to
sure to get non-polymorphic behaviour.</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">get_real_instances(base_objects_list_or_queryset)</tt> allows you to turn a
queryset or list of base model objects efficiently into the real objects.
For example, you could do <tt class="docutils literal"><span class="pre">base_objects=ModelA.base_objects.extra(...)</span></tt> and
then call <tt class="docutils literal">real_objects=ModelA.objects.get_real_instances(base_objects)</tt>.</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">values()</tt> &amp; <tt class="docutils literal">values_list()</tt> currently do not return polymorphic
results. This may change in the future however. If you want to use these
methods now, it's best if you use <tt class="docutils literal"><span class="pre">Model.base_objects.values...</span></tt> as
this is guaranteed to not change.</li>
</ul>
<ul class="simple">
<li><tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet supported (support will be added
in the future).</li>
</ul>
</div>
<div class="section" id="using-enhanced-q-objects-in-any-places">
<h2>Using enhanced Q-objects in any Places</h2>
<p>Sometimes it would be nice to be able to use the enhanced filter-definitions/Q-objects
outside of polymorphic models/querysets. Example (using <tt class="docutils literal">limit_choices_to</tt>
to filter the selection of objects in the admin):</p>
<pre class="literal-block">
class MyModel(models.Model):
somekey = model.ForeignKey(Model2A,
limit_choices_to = Q(instance_of=Model2B) )
</pre>
<p><tt class="docutils literal">instance_of</tt> is a django_polymorphic-specific enhancement of Q objects, which the
vanilla django function <tt class="docutils literal">ForeignKey</tt> cannot process. In such cases you can do:</p>
<pre class="literal-block">
from polymorphic import translate_polymorphic_Q_object
class MyModel(models.Model):
somekey = model.ForeignKey(Model2A,
limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) )
</pre>
</div>
<div class="section" id="nicely-displaying-polymorphic-querysets">
<h2>Nicely Displaying Polymorphic Querysets</h2>
<p>In order to get the output as seen in all examples here, you need to use the
ShowFieldType class mixin:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, ShowFieldType
class ModelA(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10)
</pre>
<p>You may also use ShowFieldContent or ShowFieldTypeAndContent to display
additional information when printing querysets (or converting them to text).</p>
</div>
</div>
<div class="section" id="custom-managers-querysets-manager-inheritance">
<h1><a class="toc-backref" href="#id5">Custom Managers, Querysets &amp; Manager Inheritance</a></h1>
<div class="section" id="using-a-custom-manager">
<h2>Using a Custom Manager</h2>
<p>A nice feature of Django is the possibility to define one's own custom object managers.
This is fully supported with django_polymorphic: For creating a custom polymorphic
manager class, just derive your manager from <tt class="docutils literal">PolymorphicManager</tt> instead of
<tt class="docutils literal">models.Manager</tt>. Just as with vanilla Django, in your model class, you should
explicitly add the default manager first, and then your custom manager:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, PolymorphicManager
class MyOrderedManager(PolymorphicManager):
def get_query_set(self):
return super(MyOrderedManager,self).get_query_set().order_by('some_field')
class MyModel(PolymorphicModel):
objects = PolymorphicManager() # add the default polymorphic manager first
ordered_objects = MyOrderedManager() # then add your own manager
</pre>
<p>The first manager defined ('objects' in the example) is used by
Django as automatic manager for several purposes, including accessing
related objects. It must not filter objects and it's safest to use
the plain <tt class="docutils literal">PolymorphicManager</tt> here.</p>
</div>
<div class="section" id="manager-inheritance">
<h2>Manager Inheritance</h2>
<p>Polymorphic models inherit/propagate all managers from their
base models, as long as these are polymorphic. This means that all
managers defined in polymorphic base models work just the same as if
they were defined in the new model.</p>
<p>An example (inheriting from MyModel above):</p>
<pre class="literal-block">
class MyModel2(MyModel):
pass
# Managers inherited from MyModel:
# the regular 'objects' manager and the custom 'ordered_objects' manager
&gt;&gt;&gt; MyModel2.objects.all()
&gt;&gt;&gt; MyModel2.ordered_objects.all()
</pre>
</div>
<div class="section" id="using-a-custom-queryset-class">
<h2>Using a Custom Queryset Class</h2>
<p>The <tt class="docutils literal">PolymorphicManager</tt> class accepts one initialization argument,
which is the queryset class the manager should use. Just as with vanilla Django,
you may define your own custom queryset classes. Just use PolymorphicQuerySet
instead of Django's QuerySet as the base class:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
class MyQuerySet(PolymorphicQuerySet):
def my_queryset_method(...):
...
class MyModel(PolymorphicModel):
my_objects=PolymorphicManager(MyQuerySet)
...
</pre>
</div>
</div>
<div class="section" id="performance-considerations">
<h1><a class="toc-backref" href="#id6">Performance Considerations</a></h1>
<p>The current implementation is pretty simple and does not use any
custom SQL or Django DB layer internals - it is purely based on the
standard Django ORM. Right now the query</p>
<pre class="literal-block">
result_objects = list( ModelA.objects.filter(...) )
</pre>
<p>performs one SQL query to retrieve <tt class="docutils literal">ModelA</tt> objects and one additional
query for each unique derived class occurring in result_objects.
The best case for retrieving 100 objects is 1 SQL query if all are
class <tt class="docutils literal">ModelA</tt>. If 50 objects are <tt class="docutils literal">ModelA</tt> and 50 are <tt class="docutils literal">ModelB</tt>, then
two queries are executed. The pathological worst case is 101 db queries if
result_objects contains 100 different object types (with all of them
subclasses of <tt class="docutils literal">ModelA</tt>).</p>
<p>Performance ist relative: when Django users create their own
polymorphic ad-hoc solution (without a tool like <tt class="docutils literal">django_polymorphic</tt>),
this usually results in a variation of</p>
<pre class="literal-block">
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
</pre>
<p>which has really bad performance. Relative to this, the
performance of the current <tt class="docutils literal">django_polymorphic</tt> is pretty good.
It's probably efficient enough for the majority of use cases.</p>
<p>Chunking: The implementation always requests objects in chunks of
size <tt class="docutils literal">Polymorphic_QuerySet_objects_per_request</tt>. This limits the
complexity/duration for each query, including the pathological cases.</p>
</div>
<div class="section" id="possible-optimizations">
<h1><a class="toc-backref" href="#id7">Possible Optimizations</a></h1>
<p><tt class="docutils literal">PolymorphicQuerySet</tt> can be optimized to require only one SQL query
for the queryset evaluation and retrieval of all objects.</p>
<p>Basically, what ist needed is a possibility to pull in the fields
from all relevant sub-models with one SQL query. However, some deeper
digging into the Django database layer will be required in order to
make this happen.</p>
<p>An optimized version might require an SQL database. For non-SQL databases
the implementation could fall back to the current ORM-only
implementation.</p>
<div class="section" id="sql-complexity-of-an-optimized-implementation">
<h2>SQL Complexity of an Optimized Implementation</h2>
<p>With only one SQL query, one SQL join for each possible subclass
would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).
With two SQL queries, the number of joins could be reduced to the
number of actuallly occurring subclasses in the result. A final
implementation might want to use one query only if the number of
possible subclasses (and therefore joins) is not too large, and
two queries otherwise (using the first query to determine the
actually occurring subclasses, reducing the number of joins for
the second).</p>
<p>The number of joins needed for polymorphic object retrieval might
raise concerns regarding the efficiency of these database
queries. It seems likely however, that the increased number of joins
is no problem for the supported DBM systems in all realistic use cases.
Should the number of joins of the more extreme use cases turn out to
be problematic, it is possible to split any problematic query into, for example,
two queries with only half the number of joins each.</p>
</div>
<div class="section" id="in-general">
<h2>In General</h2>
<p>Let's not forget that the above is just about optimization.
The current implementation already works well - and perhaps well
enough for the majority of applications.</p>
<p>Also, it seems that further optimization (down to one DB request)
would be restricted to a relatively small area of the code, and
be mostly independent from the rest of the module.
So it seems this optimization can be done at any later time
(like when it's needed).</p>
</div>
</div>
<div class="section" id="restrictions-caveats">
<span id="restrictions"></span><h1><a class="toc-backref" href="#id8">Restrictions &amp; Caveats</a></h1>
<ul class="simple">
<li>The queryset methods <tt class="docutils literal">values()</tt>, <tt class="docutils literal">values_list()</tt>, <tt class="docutils literal">select_related()</tt>,
<tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet fully supported (see above)</li>
</ul>
<ul class="simple">
<li>Django Admin Integration: There currently is no specific admin integration,
but it would most likely make sense to have one.</li>
</ul>
<ul class="simple">
<li>Diamond shaped inheritance: There seems to be a general problem
with diamond shaped multiple model inheritance with Django models
(tested with V1.1 - V1.3).
An example is here: <a class="reference external" href="http://code.djangoproject.com/ticket/10808">http://code.djangoproject.com/ticket/10808</a>.
This problem is aggravated when trying to enhance models.Model
by subclassing it instead of modifying Django core (as we do here
with PolymorphicModel).</li>
</ul>
<ul class="simple">
<li>The enhanced filter-definitions/Q-objects only work as arguments
for the methods of the polymorphic querysets. Please see above
for <tt class="docutils literal">translate_polymorphic_Q_object</tt>.</li>
</ul>
<ul class="simple">
<li>Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 &quot;related_name&quot;
option (fixed in Django 1.2).</li>
</ul>
<ul class="simple">
<li>Django 1.1 only - when ContentType is used in models, Django's
seralisation or fixtures cannot be used (all polymorphic models
use ContentType). This issue seems to be resolved for Django 1.2
(changeset 11863: Fixed #7052, Added support for natural keys in serialization).<ul>
<li><a class="reference external" href="http://code.djangoproject.com/ticket/7052">http://code.djangoproject.com/ticket/7052</a></li>
<li><a class="reference external" href="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</a></li>
</ul>
</li>
</ul>
<ul class="simple">
<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
in the base model (the base model directly inheriting from
PolymorphicModel). You need to be aware of this when using the
<tt class="docutils literal">dumpdata</tt> management command or any other low-level
database operations. E.g. if you rename models or apps or copy
objects from one database to another, then Django's ContentType
table needs to be corrected/copied too. This is of course generally
the case for any models using Django's ContentType.</li>
</ul>
</div>
<div class="section" id="project-status">
<h1><a class="toc-backref" href="#id9">Project Status</a></h1>
<p>Django_polymorphic works well for a considerable number of users now,
and no major problems have shown up for many months.
The API can be considered stable beginning with this release.</p>
</div>
<div class="section" id="links">
<h1><a class="toc-backref" href="#id10">Links</a></h1>
<ul class="simple">
<li><a class="reference external" href="http://code.djangoproject.com/wiki/ModelInheritance">http://code.djangoproject.com/wiki/ModelInheritance</a></li>
<li><a class="reference external" href="http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html">http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html</a></li>
<li><a class="reference external" href="http://www.djangosnippets.org/snippets/1031/">http://www.djangosnippets.org/snippets/1031/</a></li>
<li><a class="reference external" href="http://www.djangosnippets.org/snippets/1034/">http://www.djangosnippets.org/snippets/1034/</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&amp;q=model+inheritance+CORBA#a20fabc661b7035d">http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&amp;q=model+inheritance+CORBA#a20fabc661b7035d</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&amp;q=inheritance#0b92971ffc0aa6f8">http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&amp;q=inheritance#0b92971ffc0aa6f8</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&amp;q=inheritance#d8c0af3dacad412d">http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&amp;q=inheritance#d8c0af3dacad412d</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f">http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f</a></li>
<li><a class="reference external" href="http://peterbraden.co.uk/article/django-inheritance">http://peterbraden.co.uk/article/django-inheritance</a></li>
<li><a class="reference external" href="http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django">http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django</a></li>
<li><a class="reference external" href="http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982">http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982</a></li>
<li><a class="reference external" href="http://stackoverflow.com/questions/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses">http://stackoverflow.com/questions/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&amp;q=polymorphic#e676a537d735d9ef">http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&amp;q=polymorphic#e676a537d735d9ef</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&amp;q=model+inheritance#bc18c18b2e83881e">http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&amp;q=model+inheritance#bc18c18b2e83881e</a></li>
<li><a class="reference external" href="http://code.djangoproject.com/ticket/10808">http://code.djangoproject.com/ticket/10808</a></li>
<li><a class="reference external" href="http://code.djangoproject.com/ticket/7270">http://code.djangoproject.com/ticket/7270</a></li>
</ul>
</div>
</div>
</body>
</html>

377
DOCS.rst
View File

@ -1,27 +1,106 @@
Polymorphic Models for Django
=============================
.. contents:: Table of Contents
:depth: 1
Installation / Testing
======================
Quickstart
===========
Install
-------
After uncompressing (if necessary), in the directory "django_polymorphic",
execute (on Unix-like systems)::
sudo python setup.py install
Make Your Models Polymorphic
----------------------------
Use ``PolymorphicModel`` instead of Django's ``models.Model``, like so::
from polymorphic import PolymorphicModel
class Project(PolymorphicModel):
topic = models.CharField(max_length=30)
class ArtProject(Project):
artist = models.CharField(max_length=30)
class ResearchProject(Project):
supervisor = models.CharField(max_length=30)
All models inheriting from your polymorphic models will be polymorphic as well.
Create some objects
-------------------
>>> Project.objects.create(topic="John's Gathering")
>>> ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
Get polymorphic query results
-----------------------------
>>> Project.objects.all()
[ <Project: id 1, topic: "John's Gathering">,
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
using instance_of and not_instance_of for narrowing the result to specific subtypes:
>>> Project.objects.instance_of(ArtProject)
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner"> ]
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
Polymorphic filtering: Let's get all projects where Mr. Turner is involved as an artist
or supervisor (note the three underscores):
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
<ResearchProject: id 3, topic: "History of Sculpting", supervisor: "T. Turner"> ]
What's More?
-------------
Most of Django's standard ORM functionality is available and works as expected.
ForeignKeys, ManyToManyFields and OneToToneFields to your polymorphic
models work as shey should (polymorphic).
In short, with django_polymorphic the Django models are much more "pythonic", i.e.
they just work as you expect them to work: very similar to ordinary python classes
(which is not the case with vanilla Django model inheritance).
Note: In all example output, above and below, for a nicer and more informative
output the `ShowFieldType` mixin has been used (documented below).
More about Installation / Testing
=================================
Requirements
------------
Django 1.1 (or later) and Python 2.5/2.6. This code has been tested
on Django 1.1.1 / 1.2 beta and Python 2.5.4 / 2.6.4 on Linux.
Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
on Django 1.1.1 / 1.2 beta and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.
Testing
-------
Included Test Suite
-------------------
The repository (or tar file) contains a complete Django project
that may be used for tests or experiments, without any installation needed.
To run the included test suite, execute::
./manage test
./manage test polymorphic
The management command ``pcmd.py`` in the app ``pexp`` (Polymorphic EXPerimenting)
can be used for experiments - modify this file (pexp/management/commands/pcmd.py)
The management command ``pcmd.py`` in the app ``pexp`` can be used for
experiments - modify this file (pexp/management/commands/pcmd.py)
to your liking, then run::
./manage syncdb # db is created in /var/tmp/... (settings.py)
@ -33,7 +112,8 @@ Installation
In the directory "django_polymorphic", execute ``sudo python setup.py install``.
Alternatively you can simply copy the ``polymorphic`` directory
(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').
If you want to use the management command ``polymorphic_dumpdata``, then
you need to add ``polymorphic`` to your INSTALLED_APPS setting. This is also
@ -43,12 +123,10 @@ In any case, Django's ContentType framework (``django.contrib.contenttypes``)
needs to be listed in INSTALLED_APPS (usually it already is).
Defining Polymorphic Models
===========================
More Polymorphic Functionality
==============================
To make models polymorphic, use ``PolymorphicModel`` instead of Django's
``models.Model`` as the superclass of your base model. All models
inheriting from your base class will be polymorphic as well::
In the examples below, these models are being used::
from polymorphic import PolymorphicModel
@ -61,29 +139,6 @@ inheriting from your base class will be polymorphic as well::
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
Using Polymorphic Models
========================
Most of Django's standard ORM functionality is available
and works as expected:
Create some objects
-------------------
>>> ModelA.objects.create(field1='A1')
>>> ModelB.objects.create(field1='B1', field2='B2')
>>> ModelC.objects.create(field1='C1', field2='C2', field3='C3')
Query results are polymorphic
-----------------------------
>>> ModelA.objects.all()
.
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Filtering for classes (equivalent to python's isinstance() ):
-------------------------------------------------------------
@ -97,6 +152,10 @@ Filtering for classes (equivalent to python's isinstance() ):
ModelA.objects.instance_of(ModelB [, ModelC ...])
ModelA.objects.not_instance_of(ModelB [, ModelC ...])
You can also use this feature in Q-objects (with the same result as above):
>>> ModelA.objects.filter( Q(instance_of=ModelB) )
Polymorphic filtering (for fields in derived classes)
-----------------------------------------------------
@ -122,27 +181,6 @@ Combining Querysets of different types/models
[ <ModelX: id 1, field_x (CharField)>,
<ModelY: id 2, field_y (CharField)> ]
Using Third Party Models (without modifying them)
-------------------------------------------------
Third party models can be used as polymorphic models without
restrictions by subclassing them. E.g. using a third party
model as the root of a polymorphic inheritance tree::
from thirdparty import ThirdPartyModel
class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
Or instead integrating the third party model anywhere into an
existing polymorphic inheritance tree::
class MyModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)
class MyModelWithThirdParty(MyModel, ThirdPartyModel):
pass # or add fields
ManyToManyField, ForeignKey, OneToOneField
------------------------------------------
@ -173,6 +211,27 @@ ManyToManyField, ForeignKey, OneToOneField
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Using Third Party Models (without modifying them)
-------------------------------------------------
Third party models can be used as polymorphic models without
restrictions by subclassing them. E.g. using a third party
model as the root of a polymorphic inheritance tree::
from thirdparty import ThirdPartyModel
class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
Or instead integrating the third party model anywhere into an
existing polymorphic inheritance tree::
class MyModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)
class MyModelWithThirdParty(MyModel, ThirdPartyModel):
pass # or add fields
Non-Polymorphic Queries
-----------------------
@ -186,30 +245,38 @@ Non-Polymorphic Queries
Django manager. Of course, arbitrary custom managers may be
added to the models as well.
More Queryset Methods
---------------------
About Queryset Methods
----------------------
+ ``annotate()`` and ``aggregate()`` work just as usual, with the
* ``annotate()`` and ``aggregate()`` work just as usual, with the
addition that the ``ModelX___field`` syntax can be used for the
keyword arguments (but not for the non-keyword arguments).
+ ``order_by()`` now similarly supports the ``ModelX___field`` syntax
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.
+ ``select_related()`` works just as usual, but it can not (yet) be used
to select relations in derived models
(like ``ModelA.objects.select_related('ModelC___fieldxy')`` )
+ ``extra()`` by default works exactly like the original version,
* ``extra()`` by default works exactly like the original version,
with the resulting queryset not being polymorphic. There is
experimental support for a polymorphic extra() via the keyword
argument ``polymorphic=True`` (only the ``where`` and
``order_by`` arguments of extra() should be used then).
``order_by`` and ``params`` arguments of extra() should be used then).
The behaviour of extra() may change in the future, so it's best if you use
``base_objects=ModelA.base_objects.extra(...)`` instead if you want to
sure to get non-polymorphic behaviour.
+ ``values()`` & ``values_list()`` currently do not return polymorphic
+ ``get_real_instances(base_objects_list_or_queryset)`` allows you to turn a
queryset or list of base model objects efficiently into the real objects.
For example, you could do ``base_objects=ModelA.base_objects.extra(...)`` and
then call ``real_objects=ModelA.objects.get_real_instances(base_objects)``.
* ``values()`` & ``values_list()`` currently do not return polymorphic
results. This may change in the future however. If you want to use these
methods now, it's best if you use ``Model.base_objects.values...`` as
this is guaranteed to not change.
@ -217,32 +284,52 @@ More Queryset Methods
+ ``defer()`` and ``only()`` are not yet supported (support will be added
in the future).
manage.py dumpdata
------------------
Using enhanced Q-objects in any Places
--------------------------------------
Django's standard ``dumpdata`` command requires non-polymorphic
behaviour from the querysets it uses and produces incomplete
results with polymorphic models. Django_polymorphic includes
a slightly modified version, named ``polymorphic_dumpdata``
that fixes this. Just use this command instead of Django's
(see "installation/testing").
Sometimes it would be nice to be able to use the enhanced filter-definitions/Q-objects
outside of polymorphic models/querysets. Example (using ``limit_choices_to``
to filter the selection of objects in the admin)::
Please note that there are problems using ContentType together
with Django's seralisation or fixtures (and all polymorphic models
use ContentType). This issue seems to be resolved with Django 1.2
(changeset 11863): http://code.djangoproject.com/ticket/7052
class MyModel(models.Model):
somekey = model.ForeignKey(Model2A,
limit_choices_to = Q(instance_of=Model2B) )
``instance_of`` is a django_polymorphic-specific enhancement of Q objects, which the
vanilla django function ``ForeignKey`` cannot process. In such cases you can do::
from polymorphic import translate_polymorphic_Q_object
class MyModel(models.Model):
somekey = model.ForeignKey(Model2A,
limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) )
Nicely Displaying Polymorphic Querysets
---------------------------------------
In order to get the output as seen in all examples here, you need to use the
ShowFieldType class mixin::
from polymorphic import PolymorphicModel, ShowFieldType
class ModelA(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10)
You may also use ShowFieldContent or ShowFieldTypeAndContent to display
additional information when printing querysets (or converting them to text).
Custom Managers, Querysets & Inheritance
========================================
Custom Managers, Querysets & Manager Inheritance
================================================
Using a Custom Manager
----------------------
For creating a custom polymorphic manager class, derive your manager
from ``PolymorphicManager`` instead of ``models.Manager``. In your model
class, explicitly add the default manager first, and then your
custom manager::
A nice feature of Django is the possibility to define one's own custom object managers.
This is fully supported with django_polymorphic: For creating a custom polymorphic
manager class, just derive your manager from ``PolymorphicManager`` instead of
``models.Manager``. Just as with vanilla Django, in your model class, you should
explicitly add the default manager first, and then your custom manager::
from polymorphic import PolymorphicModel, PolymorphicManager
@ -281,8 +368,9 @@ Using a Custom Queryset Class
-----------------------------
The ``PolymorphicManager`` class accepts one initialization argument,
which is the queryset class the manager should use. A custom
custom queryset class can be defined and used like this::
which is the queryset class the manager should use. Just as with vanilla Django,
you may define your own custom queryset classes. Just use PolymorphicQuerySet
instead of Django's QuerySet as the base class::
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
@ -299,20 +387,18 @@ Performance Considerations
==========================
The current implementation is pretty simple and does not use any
custom SQL - it is purely based on the Django ORM. Right now the
query ::
custom SQL or Django DB layer internals - it is purely based on the
standard Django ORM. Right now the query ::
result_objects = list( ModelA.objects.filter(...) )
performs one SQL query to retrieve ``ModelA`` objects and one additional
query for each unique derived class occurring in result_objects.
The best case for retrieving 100 objects is 1 db query if all are
The best case for retrieving 100 objects is 1 SQL query if all are
class ``ModelA``. If 50 objects are ``ModelA`` and 50 are ``ModelB``, then
two queries are executed. If result_objects contains only the base model
type (``ModelA``), the polymorphic models are just as efficient as plain
Django models (in terms of executed queries). The pathological worst
case is 101 db queries if result_objects contains 100 different
object types (with all of them subclasses of ``ModelA``).
two queries are executed. The pathological worst case is 101 db queries if
result_objects contains 100 different object types (with all of them
subclasses of ``ModelA``).
Performance ist relative: when Django users create their own
polymorphic ad-hoc solution (without a tool like ``django_polymorphic``),
@ -322,7 +408,7 @@ this usually results in a variation of ::
which has really bad performance. Relative to this, the
performance of the current ``django_polymorphic`` is pretty good.
It may well be efficient enough for the majority of use cases.
It's probably efficient enough for the majority of use cases.
Chunking: The implementation always requests objects in chunks of
size ``Polymorphic_QuerySet_objects_per_request``. This limits the
@ -340,16 +426,12 @@ from all relevant sub-models with one SQL query. However, some deeper
digging into the Django database layer will be required in order to
make this happen.
A viable option might be to get the SQL query from the QuerySet
(probably from ``django.db.models.SQL.compiler.SQLCompiler.as_sql``),
making sure that all necessary joins are done, and then doing a
custom SQL request from there (like in ``SQLCompiler.execute_sql``).
An optimized version might require an SQL database. For non-SQL databases
the implementation could fall back to the current ORM-only
implementation.
An optimized version could fall back to the current ORM-only
implementation for all non-SQL databases.
SQL Complexity
--------------
SQL Complexity of an Optimized Implementation
---------------------------------------------
With only one SQL query, one SQL join for each possible subclass
would be needed (``BaseModel.__subclasses__()``, recursively).
@ -361,21 +443,18 @@ two queries otherwise (using the first query to determine the
actually occurring subclasses, reducing the number of joins for
the second).
A relatively large number of joins may be needed in both cases,
which raises concerns regarding the efficiency of these database
queries. It is currently unclear however, how many model classes
will actually be involved in typical use cases - the total number
of classes in the inheritance tree as well as the number of distinct
classes in query results. It may well turn out that the increased
number of joins is no problem for the DBMS in all realistic use
cases. Alternatively, if the SQL query execution time is
significantly longer even in common use cases, this may still be
acceptable in exchange for the added functionality.
The number of joins needed for polymorphic object retrieval might
raise concerns regarding the efficiency of these database
queries. It seems likely however, that the increased number of joins
is no problem for the supported DBM systems in all realistic use cases.
Should the number of joins of the more extreme use cases turn out to
be problematic, it is possible to split any problematic query into, for example,
two queries with only half the number of joins each.
In General
----------
Let's not forget that all of the above is just about optimization.
Let's not forget that the above is just about optimization.
The current implementation already works well - and perhaps well
enough for the majority of applications.
@ -386,18 +465,35 @@ So it seems this optimization can be done at any later time
(like when it's needed).
.. _restrictions:
Restrictions & Caveats
======================
* The queryset methods ``values()``, ``values_list()``, ``select_related()``,
``defer()`` and ``only()`` are not yet fully supported (see above)
+ Django Admin Integration: There currently is no specific admin integration,
but it would most likely make sense to have one.
* Diamond shaped inheritance: There seems to be a general problem
with diamond shaped multiple model inheritance with Django models
(tested with V1.1 - V1.3).
An example is here: http://code.djangoproject.com/ticket/10808.
This problem is aggravated when trying to enhance models.Model
by subclassing it instead of modifying Django core (as we do here
with PolymorphicModel).
+ The enhanced filter-definitions/Q-objects only work as arguments
for the methods of the polymorphic querysets. Please see above
for ``translate_polymorphic_Q_object``.
* Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 "related_name"
option (fixed in Django 1.2).
* Django 1.1 only - when ContentType is used in models, Django's
+ Django 1.1 only - when ContentType is used in models, Django's
seralisation or fixtures cannot be used (all polymorphic models
use ContentType). This issue seems to be resolved for Django 1.2
(changeset 11863: Fixed #7052, Added support for natural keys in serialization).
@ -405,54 +501,23 @@ Restrictions & Caveats
+ http://code.djangoproject.com/ticket/7052
+ http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django
* Diamond shaped inheritance: There seems to be a general problem
with diamond shaped multiple model inheritance with Django models
(tested with V1.1).
An example is here: http://code.djangoproject.com/ticket/10808.
This problem is aggravated when trying to enhance models.Model
by subclassing it instead of modifying Django core (as we do here
with PolymorphicModel).
* Django Admin Integration: There currently is no admin integration,
but it surely would be nice to have one. There is a discussion about it here:
http://groups.google.de/group/django-polymorphic/browse_thread/thread/84290fe76c40c12d
* It must be possible to instantiate the base model objects, even if your
application never does this itself. This is needed by the current
implementation of polymorphic querysets but (likely) also by Django internals.
Example: If ModelB and ModelC inherit from ModelA, and you never create
ModelA objects, django_polymorphic and Django core will still instantiate
ModelA objects for temporary purposes (and fail, if this isn't possible).
* A reference (``ContentType``) to the real/leaf model is stored
in the base model (the base model directly inheriting from
PolymorphicModel). If a model or an app is renamed, then Django's
ContentType table needs to be corrected too, if the db content
should stay usable after the rename.
PolymorphicModel). You need to be aware of this when using the
``dumpdata`` 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.
* For all objects that are not instances of the base class, but
instances of a subclass, the base class fields are currently
transferred twice from the database (an artefact of the current
implementation's simplicity).
* __getattribute__ hack: For base model inheritance back relation
fields (like basemodel_ptr), as well as implicit model inheritance
forward relation fields, Django internally tries to use our
polymorphic manager/queryset in some places, which of course it
should not. Currently this is solved with a hacky __getattribute__
in PolymorphicModel, which causes some overhead. A minor patch to
Django core would probably get rid of that.
Project Status
==============
It's important to consider that this code is very new and
to some extent still experimental. It does seem to work very
well for a number of people, but API changes, code reorganisations
or further schema changes are still a possibility. There may also
remain larger bugs and problems in the code that have not yet
been found.
Django_polymorphic works well for a considerable number of users now,
and no major problems have shown up for many months.
The API can be considered stable beginning with this release.
Links

366
README.html 100644
View File

@ -0,0 +1,366 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title></title>
<style type="text/css">
h1, h2, h3, h4 {
color: #47c;
}
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
tt {
color: #080;
}
blockquote tt {
color: #000
}
.first {
margin-top: 0 }
.last {
margin-bottom: 0 }
/*
a.toc-backref {
text-decoration: none ;
color: black }
*/
dd {
margin-bottom: 0.5em }
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em }
div.footer, div.header {
font-size: smaller }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr {
width: 75% }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.line-block {
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option-argument {
font-style: italic }
span.pre {
white-space: pre }
span.problematic {
color: red }
table {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.citation {
border-left: solid thin gray ;
padding-left: 0.5ex }
table.docinfo {
margin: 2em 4em }
table.footnote {
border-left: solid thin black ;
padding-left: 0.5ex }
td, th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
th.docinfo-name, th.field-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap }
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100% }
tt, pre.literal-block, pre.doctest-block {
font-size: 115%;
line-height: 150% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<div class="section" id="polymorphic-models-for-django">
<h1>Polymorphic Models for Django</h1>
<p>.</p>
<div class="section" id="quick-start-docs-contributing">
<h2>Quick Start, Docs, Contributing</h2>
<ul class="simple">
<li><a class="reference external" href="#good-for">What is django_polymorphic good for?</a></li>
<li><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></li>
<li><a class="reference external" href="http://groups.google.de/group/django-polymorphic/topics">Release Notes, News and Discussion</a> (Google Group) or <a class="reference external" href="http://bserve.webhop.org/django_polymorphic/CHANGES.html">Changelog</a></li>
<li>Download from <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 as <a class="reference external" href="http://github.com/bconstantin/django_polymorphic/tarball/master">TGZ</a> or <a class="reference external" href="http://github.com/bconstantin/django_polymorphic/zipball/master">ZIP</a></li>
<li>Improve django_polymorphic, report issues, participate, discuss, patch or fork (<a class="reference external" href="http://github.com/bconstantin/django_polymorphic">GitHub</a>, <a class="reference external" href="http://bitbucket.org/bconstantin/django_polymorphic">Bitbucket</a>, <a class="reference external" href="http://groups.google.de/group/django-polymorphic/topics">Group</a>, <a class="reference external" href="http://github.com/bconstantin/django_polymorphic/tree/master/setup.py">Mail</a>)</li>
</ul>
</div>
<div class="section" id="id1">
<span id="good-for"></span><h2>What is django_polymorphic good for?</h2>
<p>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 &quot;pythonic&quot;
and now just works as you as a Python programmer expect.</p>
</div>
<div class="section" id="it-s-best-to-look-at-an-example">
<h2>It's best to Look at an Example</h2>
<p>Let's assume the models <tt class="docutils literal">ArtProject</tt> and <tt class="docutils literal">ResearchProject</tt> are derived
from the model <tt class="docutils literal">Project</tt>, and let's store one of each into the database:</p>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.create(topic=&quot;John's Gathering&quot;)
&gt;&gt;&gt; ArtProject.objects.create(topic=&quot;Sculpting with Tim&quot;, artist=&quot;T. Turner&quot;)
&gt;&gt;&gt; ResearchProject.objects.create(topic=&quot;Swallow Aerodynamics&quot;, supervisor=&quot;Dr. Winter&quot;)
</pre>
<p>If we want to retrieve all our projects, we do:</p>
<pre class="doctest-block">
&gt;&gt;&gt; Project.objects.all()
</pre>
<p>Using django_polymorphic, we simply get what we stored:</p>
<pre class="literal-block">
[ &lt;Project: id 1, topic: &quot;John's Gathering&quot;&gt;,
&lt;ArtProject: id 2, topic: &quot;Sculpting with Tim&quot;, artist: &quot;T. Turner&quot;&gt;,
&lt;ResearchProject: id 3, topic: &quot;Swallow Aerodynamics&quot;, supervisor: &quot;Dr. Winter&quot;&gt; ]
</pre>
<p>Using vanilla Django, we get incomplete objects, which is probably not what we wanted:</p>
<pre class="literal-block">
[ &lt;Project: id 1, topic: &quot;John's Gathering&quot;&gt;,
&lt;Project: id 2, topic: &quot;Sculpting with Tim&quot;&gt;,
&lt;Project: id 3, topic: &quot;Swallow Aerodynamics&quot;&gt; ]
</pre>
<p>It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.</p>
<p>In general, the effect of django_polymorphic is twofold:</p>
<p>On one hand it makes sure that model inheritance just works
as you expect, by simply ensuring that you always get back exactly the same
objects from the database you stored there - regardless how you access them.
This can save you a lot of unpleasant workarounds.</p>
<p>On the other hand, together with only few small API additions to the Django ORM,
django_polymorphic enables a much more expressive and intuitive
programming style and also very advanced object oriented
designs that are not possible with vanilla Django.</p>
<p>Fortunately, most of the heavy duty machinery that is needed for this
functionality is already present in the original Django database layer.
Django_polymorphic merely adds a rather thin layer above that, which is
all that is required to make real OO fully automatic and very easy to use,
with only minimal additions to Django's API.</p>
<p>For more information, please look at <a class="reference external" href="http://bserve.webhop.org/django_polymorphic/DOCS.html#quickstart">Quickstart</a> or the complete
<a class="reference external" href="http://bserve.webhop.org/django_polymorphic/DOCS.html">Installation and Usage Docs</a>. Please also see the <a class="reference external" href="http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions">restrictions and caveats</a>.</p>
</div>
<div class="section" id="this-is-a-v1-0-beta-testing-release">
<h2>This is a V1.0 Beta/Testing Release</h2>
<p>This release is mostly a cleanup and maintenance release that also
improves a number of minor things and fixes one (non-critical) bug.</p>
<p>Some pending API changes and corrections have been folded into this release
in order to make the upcoming V1.0 API as stable as possible.</p>
<p>This release is also about getting feedback from you in case you don't
approve of any of these changes or would like to get additional
API fixes into V1.0.</p>
<p>The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
potential problems to turn up (if they exist).</p>
<p>If you encounter any problems please post them in the <a class="reference external" href="http://groups.google.de/group/django-polymorphic/topics">discussion group</a>
or open an issue on <a class="reference external" href="http://github.com/bconstantin/django_polymorphic">GitHub</a> or <a class="reference external" href="http://bitbucket.org/bconstantin/django_polymorphic">BitBucket</a> (or send me an email).</p>
</div>
</div>
<div class="section" id="license">
<h1>License</h1>
<p>Django_polymorphic uses the same license as Django (BSD-like).</p>
</div>
<div class="section" id="api-changes">
<h1>API Changes</h1>
<div class="section" id="october-18-2010">
<h2>October 18 2010</h2>
<div class="section" id="polymorphic-dumpdata">
<h3>polymorphic_dumpdata</h3>
<p>The polymorphic_dumpdata management command is not needed anymore
and has been removed, as the regular Django dumpdata command now automatically
works correctly with polymorphic models (for all supported versions of Django).</p>
</div>
<div class="section" id="output-of-queryset-or-object-printing">
<h3>Output of Queryset or Object Printing</h3>
<p>In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
To get the old behaviour when printing querysets, you need to replace your model definition:</p>
<pre class="doctest-block">
&gt;&gt;&gt; class Project(PolymorphicModel):
</pre>
<p>by:</p>
<pre class="doctest-block">
&gt;&gt;&gt; class Project(PolymorphicModel, ShowFieldType):
</pre>
<p>The mixin classes for pretty output have been renamed:</p>
<blockquote>
<tt class="docutils literal">ShowFieldTypes, ShowFields, ShowFieldsAndTypes</tt></blockquote>
<p>are now:</p>
<blockquote>
<tt class="docutils literal">ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent</tt></blockquote>
<p>(the old ones still exist for compatibility)</p>
</div>
<div class="section" id="running-the-test-suite-with-django-1-3">
<h3>Running the Test suite with Django 1.3</h3>
<p>Django 1.3 requires <tt class="docutils literal">python manage.py test polymorphic</tt> instead of
just <tt class="docutils literal">python manage.py test</tt>.</p>
</div>
</div>
<div class="section" id="february-22-2010-installation-note">
<h2>February 22 2010, Installation Note</h2>
<p>The django_polymorphic source code has been restructured
and as a result needs to be installed like a normal Django App
- either via copying the &quot;polymorphic&quot; directory into your
Django project or by running setup.py. Adding 'polymorphic'
to INSTALLED_APPS in settings.py is still optional, however.</p>
<p>The file <cite>polymorphic.py</cite> cannot be used as a standalone
extension module anymore (as is has been split into a number
of smaller files).</p>
<p>Importing works slightly different now: All relevant symbols are
imported directly from 'polymorphic' instead from
'polymorphic.models':</p>
<pre class="literal-block">
# new way
from polymorphic import PolymorphicModel, ...
# old way, doesn't work anymore
from polymorphic.models import PolymorphicModel, ...
</pre>
</div>
<div class="section" id="january-26-2010-database-schema-change">
<h2>January 26 2010: Database Schema Change</h2>
<div class="line-block">
<div class="line">The update from January 26 changed the database schema (more info in the <a class="reference external" href="http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7">commit-log</a>).</div>
<div class="line">Sorry for any inconvenience. But this should be the final DB schema now.</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,79 +1,168 @@
Release Notes, Usage, Code
--------------------------
Polymorphic Models for Django
=============================
* Please see `here for release notes, news and discussion`_ (Google Group)
* `Many Examples`_, or full `Installation and Usage Docs`_ (or the short `Overview`_)
.
Quick Start, Docs, Contributing
-------------------------------
* `What is django_polymorphic good for?`_
* `Quickstart`_, or the complete `Installation and Usage Docs`_
* `Release Notes, News and Discussion`_ (Google Group) or Changelog_
* Download from GitHub_ or Bitbucket_, or as TGZ_ or ZIP_
* Improve django_polymorphic: Report issues, discuss, post patch, or fork (GitHub_, Bitbucket_, Group_, Mail_)
* Improve django_polymorphic, report issues, participate, discuss, patch or fork (GitHub_, Bitbucket_, Group_, Mail_)
.. _here for release notes, news and discussion: http://groups.google.de/group/django-polymorphic/topics
.. _What is django_polymorphic good for?: #good-for
.. _release notes, news and discussion: http://groups.google.de/group/django-polymorphic/topics
.. _Group: http://groups.google.de/group/django-polymorphic/topics
.. _Mail: http://github.com/bconstantin/django_polymorphic/tree/master/setup.py
.. _Installation and Usage Docs: http://bserve.webhop.org/wiki/django_polymorphic/doc
.. _Many Examples: http://bserve.webhop.org/wiki/django_polymorphic/doc#defining-polymorphic-models
.. _Installation and Usage Docs: http://bserve.webhop.org/django_polymorphic/DOCS.html
.. _Quickstart: http://bserve.webhop.org/django_polymorphic/DOCS.html#quickstart
.. _GitHub: http://github.com/bconstantin/django_polymorphic
.. _Bitbucket: http://bitbucket.org/bconstantin/django_polymorphic
.. _TGZ: http://github.com/bconstantin/django_polymorphic/tarball/master
.. _ZIP: http://github.com/bconstantin/django_polymorphic/zipball/master
.. _Overview: http://bserve.webhop.org/wiki/django_polymorphic
.. _Overview: http://bserve.webhop.org/django_polymorphic
.. _Changelog: http://bserve.webhop.org/django_polymorphic/CHANGES.html
.. _good-for:
What is django_polymorphic good for?
------------------------------------
**Example**: If we define the model ``Project`` as the base class for
our models ``ArtProject`` and ``ResearchProject``, and we store one of
each into the database, then we can do::
If you work with Django's model inheritance, django_polymorphic might
save you from implementing unpleasant workarounds that make your code
messy, error-prone, and slow. Model inheritance becomes much more "pythonic"
and now just works as you as a Python programmer expect.
It's best to Look at an Example
-------------------------------
Let's assume the models ``ArtProject`` and ``ResearchProject`` are derived
from the model ``Project``, and let's store one of each into the database:
>>> Project.objects.create(topic="John's Gathering")
>>> ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
If we want to retrieve all our projects, we do:
>>> Project.objects.all()
.
Using django_polymorphic, we simply get what we stored::
[ <Project: id 1, topic: "John's Gathering">,
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
In general: django_polymorphic implements seamless polymorphic inheritance for Django models.
Using vanilla Django, we get incomplete objects, which is probably not what we wanted::
The effect: objects are always returned back from the database just
as you created them, with the same type/class and fields.
[ <Project: id 1, topic: "John's Gathering">,
<Project: id 2, topic: "Sculpting with Tim">,
<Project: id 3, topic: "Swallow Aerodynamics"> ]
It doesn't matter how these objects are retrieved: be it through the
model's own managers/querysets, ForeignKeys, ManyToManyFields
or OneToOneFields.
It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.
As seen in this example, the resulting querysets are polymorphic,
and will typically deliver objects of several different types in
a single query result.
In general, the effect of django_polymorphic is twofold:
django_polymorphic does this only for models that explicitely enable it
(and for their submodels).
On one hand it makes sure that model inheritance just works
as you expect, by simply ensuring that you always get back exactly the same
objects from the database you stored there - regardless how you access them.
This can save you a lot of unpleasant workarounds.
Please see the `Documentation and Examples`_ for more information
or directly look at `more Examples`_.
On the other hand, together with only few small API additions to the Django ORM,
django_polymorphic enables a much more expressive and intuitive
programming style and also very advanced object oriented
designs that are not possible with vanilla Django.
.. _Documentation and Examples: http://bserve.webhop.org/wiki/django_polymorphic/doc
.. _more Examples: http://bserve.webhop.org/wiki/django_polymorphic/doc#defining-polymorphic-models
Fortunately, most of the heavy duty machinery that is needed for this
functionality is already present in the original Django database layer.
Django_polymorphic merely adds a rather thin layer above that, which is
all that is required to make real OO fully automatic and very easy to use,
with only minimal additions to Django's API.
Status
------
For more information, please look at `Quickstart`_ or the complete
`Installation and Usage Docs`_. Please also see the `restrictions and caveats`_.
It's important to consider that this code is very new and
to some extent still experimental. Please see the docs for
current restrictions, caveats, and performance implications.
.. _restrictions and caveats: http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions
It does seem to work very well for a number of people, but
API changes, code reorganisations or further schema changes
are still a possibility. There may also remain larger bugs
and problems in the code that have not yet been found.
This is a V1.0 Beta/Testing Release
-----------------------------------
This release is mostly a cleanup and maintenance release that also
improves a number of minor things and fixes one (non-critical) bug.
Some pending API changes and corrections have been folded into this release
in order to make the upcoming V1.0 API as stable as possible.
This release is also about getting feedback from you in case you don't
approve of any of these changes or would like to get additional
API fixes into V1.0.
The release contains a considerable amount of changes in some of the more
critical parts of the software. It's intended for testing and development
environments and not for production environments. For these, it's best to
wait a few weeks for the proper V1.0 release, to allow some time for any
potential problems to turn up (if they exist).
If you encounter any problems please post them in the `discussion group`_
or open an issue on GitHub_ or BitBucket_ (or send me an email).
.. _discussion group: http://groups.google.de/group/django-polymorphic/topics
License
-------
=======
django_polymorphic uses the same license as Django (BSD-like).
Django_polymorphic uses the same license as Django (BSD-like).
API Change on February 22, plus Installation Note
-------------------------------------------------
API Changes
===========
October 18 2010
-------------------------------------------------------------------
polymorphic_dumpdata
++++++++++++++++++++
The polymorphic_dumpdata management command is not needed anymore
and has been removed, as the regular Django dumpdata command now automatically
works correctly with polymorphic models (for all supported versions of Django).
Output of Queryset or Object Printing
+++++++++++++++++++++++++++++++++++++
In order to improve compatibility with vanilla Django, printing quersets does not use
django_polymorphic's pretty printing by default anymore.
To get the old behaviour when printing querysets, you need to replace your model definition:
>>> class Project(PolymorphicModel):
by:
>>> class Project(PolymorphicModel, ShowFieldType):
The mixin classes for pretty output have been renamed:
``ShowFieldTypes, ShowFields, ShowFieldsAndTypes``
are now:
``ShowFieldType, ShowFieldContent and ShowFieldTypeAndContent``
(the old ones still exist for compatibility)
Running the Test suite with Django 1.3
++++++++++++++++++++++++++++++++++++++
Django 1.3 requires ``python manage.py test polymorphic`` instead of
just ``python manage.py test``.
February 22 2010, Installation Note
-----------------------------------
The django_polymorphic source code has been restructured
and as a result needs to be installed like a normal Django App
@ -96,8 +185,8 @@ imported directly from 'polymorphic' instead from
from polymorphic.models import PolymorphicModel, ...
Database Schema Change on January 26
------------------------------------
January 26 2010: Database Schema Change
-----------------------------------------
| The update from January 26 changed the database schema (more info in the commit-log_).
| Sorry for any inconvenience. But this should be the final DB schema now.

8
diffmanagement 100755
View File

@ -0,0 +1,8 @@
#!/bin/bash
colordiff -u -w libraries-local/django-versions/django1.1/core/management/commands/dumpdata.py polymorphic/management/commands/polymorphic_dumpdata_11.py
colordiff -u -w libraries-local/django-versions/django1.2/core/management/commands/dumpdata.py polymorphic/management/commands/polymorphic_dumpdata_12.py
colordiff -u -w libraries-local/django-versions/django1.3/core/management/commands/dumpdata.py polymorphic/management/commands/polymorphic_dumpdata_13.py

View File

@ -39,5 +39,3 @@ class Command(NoArgsCommand):
print Project.objects.all()
print

View File

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
"""
This module is a scratchpad for general development, testing & debugging
"""
from django.core.management.base import NoArgsCommand
from django.db.models import connection
from pprint import pprint
import settings
import sys
from pexp.models import *
num_objects=15000
def reset_queries():
connection.queries=[]
def show_queries():
print; print 'QUERIES:',len(connection.queries); pprint(connection.queries); print; reset_queries()
import time
###################################################################################
### benchmark wrappers
def print_timing(func, message='', iterations=1):
def wrapper(*arg):
results=[]
reset_queries()
for i in xrange(iterations):
t1 = time.time()
x = func(*arg)
t2 = time.time()
results.append((t2-t1)*1000.0)
res_sum=0
for r in results: res_sum +=r
median = res_sum / len(results)
print '%s%-19s: %.0f ms, %i queries' % (
message,func.func_name,
median,
len(connection.queries)/len(results)
)
sys.stdout.flush()
return wrapper
def run_vanilla_any_poly(func, iterations=1):
f=print_timing(func,' ', iterations)
f(nModelC)
f=print_timing(func,'poly ', iterations)
f(ModelC)
###################################################################################
### benchmarks
def bench_create(model):
for i in xrange(num_objects):
model.objects.create(field1='abc'+str(i), field2='abcd'+str(i), field3='abcde'+str(i))
#print 'count:',model.objects.count()
def bench_load1(model):
for o in model.objects.all():
pass
def bench_load1_short(model):
for i in xrange(num_objects/100):
for o in model.objects.all()[:100]:
pass
def bench_load2(model):
for o in model.objects.all():
f1=o.field1
f2=o.field2
f3=o.field3
def bench_load2_short(model):
for i in xrange(num_objects/100):
for o in model.objects.all()[:100]:
f1=o.field1
f2=o.field2
f3=o.field3
def bench_delete(model):
model.objects.all().delete()
###################################################################################
### Command
class Command(NoArgsCommand):
help = ""
def handle_noargs(self, **options):
print 'polybench - sqlite test db is stored in:',settings.SQLITE_DB_PATH
func_list = [
( bench_delete, 1 ),
( bench_create, 1 ),
( bench_load1, 5 ),
( bench_load1_short, 5 ),
( bench_load2, 5 ),
( bench_load2_short, 5 )
]
for f,iterations in func_list:
run_vanilla_any_poly(f,iterations=iterations)
print

View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
"""
This module is a scratchpad for general development, testing & debugging
"""
from django.core.management.base import NoArgsCommand
from django.db.models import connection
from pprint import pprint
import settings
from pexp.models import *
def reset_queries():
connection.queries=[]
def show_queries():
print; print 'QUERIES:',len(connection.queries); pprint(connection.queries); print; connection.queries=[]
class Command(NoArgsCommand):
help = ""
def handle_noargs(self, **options):
#print 'polycmd - sqlite test db is stored in:',settings.SQLITE_DB_PATH
print
Project.objects.all().delete()
o=Project.objects.create(topic="John's gathering")
o=ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
o=ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
print Project.objects.all()
print

View File

@ -12,10 +12,10 @@ from manager import PolymorphicManager
from query import PolymorphicQuerySet
from query_translate import translate_polymorphic_Q_object
from showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
#from showfields import ShowFieldTypes, ShowFields, ShowFieldsAndTypes # import old names for compatibility
from showfields import ShowFields, ShowFieldTypes, ShowFieldsAndTypes # import old names for compatibility
VERSION = (0, 5, 0, 'beta')
VERSION = (1, 0 , 0, 'beta')
def get_version():
version = '%s.%s' % VERSION[0:2]

View File

@ -72,19 +72,23 @@ class PolymorphicQuerySet(QuerySet):
a.lookup = translate_polymorphic_field_path(self.model, a.lookup)
def annotate(self, *args, **kwargs):
"""translate the field paths in the kwargs, then call vanilla annotate.
"""translate the polymorphic field paths in the kwargs, then call vanilla annotate.
_get_real_instances will do the rest of the job after executing the query."""
self._process_aggregate_args(args, kwargs)
return super(PolymorphicQuerySet, self).annotate(*args, **kwargs)
def aggregate(self, *args, **kwargs):
"""translate the field paths in the kwargs, then call vanilla aggregate.
"""translate the polymorphic field paths in the kwargs, then call vanilla aggregate.
We need no polymorphic object retrieval for aggregate => switch it off."""
self._process_aggregate_args(args, kwargs)
self.polymorphic_disabled = True
return super(PolymorphicQuerySet, self).aggregate(*args, **kwargs)
def extra(self, *args, **kwargs):
"""only return polymorphic results if we get "polymorphic=True" as a keyword arg."""
#for key in kwargs.keys():
# if key not in ['where','order_by', 'params']:
# assert False,"""django_polymorphic: extras() does not support keyword argument %s.
self.polymorphic_disabled = not bool(kwargs.pop('polymorphic', False))
return super(PolymorphicQuerySet, self).extra(*args, **kwargs)

View File

@ -40,6 +40,12 @@ class ModelShow3(ShowFieldTypeAndContent, PolymorphicModel):
field1 = models.CharField(max_length=10)
m2m = models.ManyToManyField('self')
class ModelShow1_plain(PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelShow2_plain(ModelShow1_plain):
field2 = models.CharField(max_length=10)
class Base(ShowFieldType, PolymorphicModel):
field_b = models.CharField(max_length=10)
class ModelX(Base):
@ -244,7 +250,7 @@ __test__ = {"doctest": """
>>> settings.DEBUG=True
>>> get_version()
'0.5 beta'
'1.0 rc1'
### simple inheritance
@ -253,7 +259,6 @@ __test__ = {"doctest": """
>>> o=Model2B.objects.create(field1='B1', field2='B2')
>>> o=Model2C.objects.create(field1='C1', field2='C2', field3='C3')
>>> Model2A.objects.all()
[ <Model2A: id 1, field1 (CharField)>,
<Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
@ -288,6 +293,12 @@ __test__ = {"doctest": """
>>> ModelShow3.objects.all().annotate(Count('m2m'))
[ <ModelShow3: id 1, field1 (CharField): "abc", m2m (ManyToManyField): 1 - Ann: m2m__count (int): "1"> ]
# no pretty printing
>>> o=ModelShow1_plain.objects.create(field1='abc')
>>> o=ModelShow2_plain.objects.create(field1='abc', field2='def')
>>> ModelShow1_plain.objects.all()
[<ModelShow1_plain: ModelShow1_plain object>, <ModelShow2_plain: ModelShow2_plain object>]
### extra() method

19
rst-to-html.py 100755
View File

@ -0,0 +1,19 @@
#!/usr/bin/python
import sys,os
css='--stylesheet-path=rst.css'
def conv(name):
if len(sys.argv)>1:
os.system('rst2html.py '+css+' %s.rst >%s.html' % (name, name) )
else:
os.system('rst2html.py '+css+' %s.rst >%s.html ; firefox %s.html' % (name, name, name) )
conv('DOCS')
conv('README')
conv('CHANGES')
sys.exit()

197
rst.css 100644
View File

@ -0,0 +1,197 @@
h1, h2, h3, h4 {
color: #47c;
}
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
tt {
color: #080;
}
blockquote tt {
color: #000
}
.first {
margin-top: 0 }
.last {
margin-bottom: 0 }
/*
a.toc-backref {
text-decoration: none ;
color: black }
*/
dd {
margin-bottom: 0.5em }
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em }
div.footer, div.header {
font-size: smaller }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr {
width: 75% }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.line-block {
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option-argument {
font-style: italic }
span.pre {
white-space: pre }
span.problematic {
color: red }
table {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.citation {
border-left: solid thin gray ;
padding-left: 0.5ex }
table.docinfo {
margin: 2em 4em }
table.footnote {
border-left: solid thin black ;
padding-left: 0.5ex }
td, th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
th.docinfo-name, th.field-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap }
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100% }
tt, pre.literal-block, pre.doctest-block {
font-size: 115%;
line-height: 150% }
ul.auto-toc {
list-style-type: none }

View File

@ -9,11 +9,26 @@ ADMINS = (
MANAGERS = ADMINS
import django
import os
if os.path.ismount('/ram'):
SQLITE_DB_PATH = '/ram/django-polymorphic-test-db.sqlite3'
else:
SQLITE_DB_PATH = '/var/tmp/django-polymorphic-test-db.sqlite3'
if django.VERSION[:2][0]>=1 and django.VERSION[:2][1]>=3:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/var/tmp/django-polymorphic-test-db.sqlite3'
}
}
else:
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = '/var/tmp/django-polymorphic-test-db.sqlite3' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.

View File

@ -1,14 +0,0 @@
#!/bin/bash
function testit {
if which $1 ; then
if ! $1 manage.py test ; then echo ERROR ; exit 10 ; fi
else
echo "### $1 is not installed!"
fi
}
testit python2.4
testit python2.5
testit python2.6

62
test_all_versions 100755
View File

@ -0,0 +1,62 @@
#!/bin/bash
# this test script runs "./manage.py test" for
# all supported python versions (2.4, 2.5, 2.6)
# and all supported Django versions (1.1, 1.2, 1.3)
# it needs symbolic links named "django1.1" and "django1.2" etc. in:
# libraries-local/django-versions
# which point to the respective django versions
cd libraries-local
rm -f django-orig
if test -e django ; then mv django django-orig ; fi
cd ..
function restore_django {
echo "### restoring original libraries-local/django"
cd libraries-local
if test -e django-orig ; then mv django-orig django ; fi
cd .
}
function test_python_version {
if which python$1 ; then
if ! python$1 manage.py test ; then
echo ERROR
restore_django
exit 10
fi
else
echo
echo "### python $1 is not installed!"
echo
fi
}
function test_all_python_versions {
test_python_version 2.4
test_python_version 2.5
test_python_version 2.6
}
function test_django_version {
if ! test -e libraries-local/django-versions/django$1 ; then
echo
echo "### django $1 is not installed!"
echo
return
fi
cd libraries-local
rm -f django
ln -s django-versions/django$1 django
cd ..
test_all_python_versions
}
test_django_version 1.1
test_django_version 1.2
test_django_version 1.3
restore_django

26
test_dumpdata 100755
View File

@ -0,0 +1,26 @@
#!/bin/bash
rm -f /var/tmp/django-polymorphic-test-db.sqlite3
rm -f /ram/django-polymorphic-test-db.sqlite3
TMPFILE=/tmp/django-polymorphic-test.dump
PYCMD="python$1"
echo
echo "#####################################################################"
echo "### Testing dumpdata"
echo
$PYCMD ./manage.py syncdb
$PYCMD ./manage.py polymorphic_create_test_data
$PYCMD ./manage.py dumpdata --indent=4 pexp >$TMPFILE
if ! diff -w $TMPFILE pexp/dumpdata_test_correct_output.txt ; then
echo "#####################################################################"
echo "ERROR: test_dumpdata failed!"
exit 10
fi
echo "#####################################################################"
echo 'SUCCESS!'