updated docs, tests, misc
This commit is contained in:
@@ -7,13 +7,22 @@
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
h1, h2, h3, h4,
|
||||
#table-of-contents
|
||||
{
|
||||
color: #47c;
|
||||
}
|
||||
h1 { padding-top: 15px; }
|
||||
h2 { padding-top: 10px; }
|
||||
h3 { padding-top: 7px; }
|
||||
|
||||
a:hover { border-bottom: 1px solid #0066cc; }
|
||||
a {color: #0066cc; text-decoration: none;}
|
||||
|
||||
li {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
tt {
|
||||
color: #080;
|
||||
@@ -218,14 +227,15 @@ ul.auto-toc {
|
||||
<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 & 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 & Caveats</a></li>
|
||||
<li><a class="reference internal" href="#project-status" id="id9">Project Status</a></li>
|
||||
<li><a class="reference internal" href="#links" id="id10">Links</a></li>
|
||||
<li><a class="reference internal" href="#list-of-features" id="id3">List of Features</a></li>
|
||||
<li><a class="reference internal" href="#more-about-installation-testing" id="id4">More about Installation / Testing</a></li>
|
||||
<li><a class="reference internal" href="#more-polymorphic-functionality" id="id5">More Polymorphic Functionality</a></li>
|
||||
<li><a class="reference internal" href="#custom-managers-querysets-manager-inheritance" id="id6">Custom Managers, Querysets & Manager Inheritance</a></li>
|
||||
<li><a class="reference internal" href="#performance-considerations" id="id7">Performance Considerations</a></li>
|
||||
<li><a class="reference internal" href="#possible-optimizations" id="id8">Possible Optimizations</a></li>
|
||||
<li><a class="reference internal" href="#restrictions-caveats" id="id9">Restrictions & Caveats</a></li>
|
||||
<li><a class="reference internal" href="#project-status" id="id10">Project Status</a></li>
|
||||
<li><a class="reference internal" href="#links" id="id11">Links</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,7 +243,7 @@ ul.auto-toc {
|
||||
<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 "django_polymorphic",
|
||||
<p>After uncompressing (if necessary), in the directory "...django_polymorphic",
|
||||
execute (on Unix-like systems):</p>
|
||||
<pre class="literal-block">
|
||||
sudo python setup.py install
|
||||
@@ -268,57 +278,66 @@ class ResearchProject(Project):
|
||||
<h2>Get polymorphic query results</h2>
|
||||
<pre class="doctest-block">
|
||||
>>> Project.objects.all()
|
||||
[ <Project: id 1, topic: "John's Gathering">,
|
||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
||||
[ <Project: id 1, topic "John's Gathering">,
|
||||
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||
</pre>
|
||||
<p>using instance_of and not_instance_of for narrowing the result to specific subtypes:</p>
|
||||
<p>use <tt class="docutils literal">instance_of</tt> or <tt class="docutils literal">not_instance_of</tt> for narrowing the result to specific subtypes:</p>
|
||||
<pre class="doctest-block">
|
||||
>>> Project.objects.instance_of(ArtProject)
|
||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner"> ]
|
||||
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner"> ]
|
||||
</pre>
|
||||
<pre class="doctest-block">
|
||||
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
|
||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
||||
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||
</pre>
|
||||
<p>Polymorphic filtering: Let's get all projects where Mr. Turner is involved as an artist
|
||||
<p>Polymorphic filtering: Get all projects where Mr. Turner is involved as an artist
|
||||
or supervisor (note the three underscores):</p>
|
||||
<pre class="doctest-block">
|
||||
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
|
||||
[ <ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
||||
<ResearchProject: id 3, topic: "History of Sculpting", supervisor: "T. Turner"> ]
|
||||
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||
<ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
|
||||
</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 "pythonic", i.e.
|
||||
they just work as you expect them to work: very similar to ordinary python classes
|
||||
(which is not the case with vanilla Django model inheritance).</p>
|
||||
<p>This is basically all you need to know, as django_polymorphic mostly
|
||||
works fully automatic and just delivers the expected ("pythonic") results.</p>
|
||||
<p>Note: In all example output, above and below, for a nicer and more informative
|
||||
output the <cite>ShowFieldType</cite> mixin has been used (documented below).</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="list-of-features">
|
||||
<h1><a class="toc-backref" href="#id3">List of Features</a></h1>
|
||||
<ul class="simple">
|
||||
<li>Fully automatic; generally makes sure that the same objects are returned
|
||||
from the database that were stored there, regardless how they are retrieved</li>
|
||||
<li>Full support for ForeignKeys, ManyToManyFields and OneToToneFields</li>
|
||||
<li>Filtering for classes, equivalent to python's isinstance(): instance_of(...), not_instance_of(...)</li>
|
||||
<li>Polymorphic filtering/ordering etc., allowing the use of fields of derived models ("ArtProject___artist")</li>
|
||||
<li>Support for user-defined custom managers</li>
|
||||
<li>Automatic inheritance of custom managers</li>
|
||||
<li>Support for user-defined custom queryset classes</li>
|
||||
<li>Non-polymorphic queries, if needed - with no other change in features/behaviour</li>
|
||||
<li>Combining querysets of different types/models ("qs3 = qs1 | qs2")</li>
|
||||
<li>Nice/informative display of polymorphic queryset results</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="more-about-installation-testing">
|
||||
<h1><a class="toc-backref" href="#id3">More about Installation / Testing</a></h1>
|
||||
<h1><a class="toc-backref" href="#id4">More about Installation / Testing</a></h1>
|
||||
<div class="section" id="requirements">
|
||||
<h2>Requirements</h2>
|
||||
<p>Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested
|
||||
on Django 1.1.1 / 1.2 beta and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
|
||||
<p>Django 1.1 (or later) and Python 2.4 or later. This code has been tested
|
||||
on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
|
||||
</div>
|
||||
<div class="section" id="included-test-suite">
|
||||
<h2>Included Test Suite</h2>
|
||||
<p>The repository (or tar file) contains a complete Django project
|
||||
that may be used for tests or experiments, without any installation needed.</p>
|
||||
<p>To run the included test suite, execute:</p>
|
||||
<p>To run the included test suite, in the directory "...django_polymorphic" execute:</p>
|
||||
<pre class="literal-block">
|
||||
./manage test polymorphic
|
||||
</pre>
|
||||
<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used for
|
||||
experiments - modify this file (pexp/management/commands/pcmd.py)
|
||||
<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used
|
||||
for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
|
||||
to your liking, then run:</p>
|
||||
<pre class="literal-block">
|
||||
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
||||
@@ -327,19 +346,18 @@ to your liking, then run:</p>
|
||||
</div>
|
||||
<div class="section" id="installation">
|
||||
<h2>Installation</h2>
|
||||
<p>In the directory "django_polymorphic", execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
|
||||
<p>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> directory
|
||||
<p>In the directory "...django_polymorphic", execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
|
||||
<p>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> subdirectory
|
||||
(under "django_polymorphic") into your Django project dir
|
||||
(e.g. if you want to distribute your project with more 'batteries included').</p>
|
||||
<p>If you want to use the management command <tt class="docutils literal">polymorphic_dumpdata</tt>, then
|
||||
you need to add <tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting. This is also
|
||||
needed if you want to run the test cases in <cite>polymorphic/tests.py</cite>.</p>
|
||||
<p>In any case, Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
|
||||
<p>If you want to run the test cases in <cite>polymorphic/tests.py</cite>, you need to add
|
||||
<tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting.</p>
|
||||
<p>Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
|
||||
needs to be listed in INSTALLED_APPS (usually it already is).</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="more-polymorphic-functionality">
|
||||
<h1><a class="toc-backref" href="#id4">More Polymorphic Functionality</a></h1>
|
||||
<h1><a class="toc-backref" href="#id5">More Polymorphic Functionality</a></h1>
|
||||
<p>In the examples below, these models are being used:</p>
|
||||
<pre class="literal-block">
|
||||
from polymorphic import PolymorphicModel
|
||||
@@ -383,12 +401,12 @@ syntax: <tt class="docutils literal">exact model name + three _ + field name</tt
|
||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="combining-querysets-of-different-types-models">
|
||||
<h2>Combining Querysets of different types/models</h2>
|
||||
<p>Querysets may now be regarded as object containers that allow the
|
||||
aggregation of different object types - very similar to python
|
||||
lists (as long as the objects are accessed through the manager of
|
||||
a common base class):</p>
|
||||
<div class="section" id="combining-querysets-querysets-as-object-containers">
|
||||
<h2>Combining Querysets / Querysets as "Object Containers"</h2>
|
||||
<p>Querysets could now be regarded as object containers that allow the
|
||||
aggregation of different object types, very similar to python
|
||||
lists - as long as the objects are accessed through the manager of
|
||||
a common base class:</p>
|
||||
<pre class="doctest-block">
|
||||
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
|
||||
.
|
||||
@@ -432,31 +450,43 @@ model as the root of a polymorphic inheritance tree:</p>
|
||||
<pre class="literal-block">
|
||||
from thirdparty import ThirdPartyModel
|
||||
|
||||
class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
|
||||
class MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
|
||||
pass # or add fields
|
||||
</pre>
|
||||
<p>Or instead integrating the third party model anywhere into an
|
||||
existing polymorphic inheritance tree:</p>
|
||||
<pre class="literal-block">
|
||||
class MyModel(SomePolymorphicModel):
|
||||
class MyBaseModel(SomePolymorphicModel):
|
||||
my_field = models.CharField(max_length=10)
|
||||
|
||||
class MyModelWithThirdParty(MyModel, ThirdPartyModel):
|
||||
class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
|
||||
pass # or add fields
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="non-polymorphic-queries">
|
||||
<h2>Non-Polymorphic Queries</h2>
|
||||
<pre class="doctest-block">
|
||||
>>> ModelA.base_objects.all()
|
||||
>>> qs=ModelA.objects.non_polymorphic().all()
|
||||
>>> qs
|
||||
.
|
||||
[ <ModelA: id 1, field1 (CharField)>,
|
||||
<ModelA: id 2, field1 (CharField)>,
|
||||
<ModelA: id 3, field1 (CharField)> ]
|
||||
</pre>
|
||||
<p>Each polymorphic model has 'base_objects' defined as a normal
|
||||
Django manager. Of course, arbitrary custom managers may be
|
||||
added to the models as well.</p>
|
||||
<p>If you insert <tt class="docutils literal">.non_polymorphic()</tt> anywhere into the query chain, then
|
||||
django_polymorphic will simply leave out the final step of retrieving the
|
||||
real objects, and the manager/queryset will return objects of the type of
|
||||
the base class you used for the query, like vanilla Django would
|
||||
(<tt class="docutils literal">ModelA</tt> in this example).</p>
|
||||
<p>There are no other changes in the behaviour of the queryset. For example,
|
||||
enhancements for <tt class="docutils literal">filter()</tt> or <tt class="docutils literal">instance_of()</tt> etc. still work as expected.
|
||||
If you do the final step yourself, you get the usual polymorphic result:</p>
|
||||
<pre class="doctest-block">
|
||||
>>> qs.get_real_instances()
|
||||
[ <ModelA: id 1, field1 (CharField)>,
|
||||
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="about-queryset-methods">
|
||||
<h2>About Queryset Methods</h2>
|
||||
@@ -464,43 +494,29 @@ added to the models as well.</p>
|
||||
<li><tt class="docutils literal">annotate()</tt> and <tt class="docutils literal">aggregate()</tt> work just as usual, with the
|
||||
addition that the <tt class="docutils literal">ModelX___field</tt> syntax can be used for the
|
||||
keyword arguments (but not for the non-keyword arguments).</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">order_by()</tt> now similarly supports the <tt class="docutils literal">ModelX___field</tt> syntax
|
||||
for specifying ordering through a field in a submodel.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">distinct()</tt> works as expected. It only regards the fields of
|
||||
the base class, but this should never make a difference.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">select_related()</tt> works just as usual, but it can not (yet) be used
|
||||
to select relations in derived models
|
||||
(like <tt class="docutils literal"><span class="pre">ModelA.objects.select_related('ModelC___fieldxy')</span></tt> )</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">extra()</tt> by default works exactly like the original version,
|
||||
with the resulting queryset not being polymorphic. There is
|
||||
experimental support for a polymorphic extra() via the keyword
|
||||
argument <tt class="docutils literal">polymorphic=True</tt> (only the <tt class="docutils literal">where</tt> and
|
||||
<tt class="docutils literal">order_by</tt> and <tt class="docutils literal">params</tt> arguments of extra() should be used then).
|
||||
The behaviour of extra() may change in the future, so it's best if you use
|
||||
<tt class="docutils literal"><span class="pre">base_objects=ModelA.base_objects.extra(...)</span></tt> instead if you want to
|
||||
sure to get non-polymorphic behaviour.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">get_real_instances(base_objects_list_or_queryset)</tt> allows you to turn a
|
||||
<li><tt class="docutils literal">extra()</tt> works as expected (it returns polymorphic results) but
|
||||
currently has one restriction: The resulting objects are required to have
|
||||
a unique primary key within the result set - otherwise an error is thrown
|
||||
(this case could be made to work, however it may be mostly unneeded)..
|
||||
The keyword-argument "polymorphic" is no longer supported.
|
||||
You can get back the old non-polymorphic behaviour (before V1.0)
|
||||
by using <tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra(...)</span></tt>.</li>
|
||||
<li><tt class="docutils literal">get_real_instances()</tt> allows you to turn a
|
||||
queryset or list of base model objects efficiently into the real objects.
|
||||
For example, you could do <tt class="docutils literal"><span class="pre">base_objects=ModelA.base_objects.extra(...)</span></tt> and
|
||||
then call <tt class="docutils literal">real_objects=ModelA.objects.get_real_instances(base_objects)</tt>.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
For example, you could do <tt class="docutils literal"><span class="pre">base_objects_queryset=ModelA.extra(...).non_polymorphic()</span></tt>
|
||||
and then call <tt class="docutils literal">real_objects=base_objects_queryset.get_real_instances()</tt>.Or alternatively
|
||||
.``real_objects=ModelA.objects..get_real_instances(base_objects_queryset_or_object_list)``</li>
|
||||
<li><tt class="docutils literal">values()</tt> & <tt class="docutils literal">values_list()</tt> currently do not return polymorphic
|
||||
results. This may change in the future however. If you want to use these
|
||||
methods now, it's best if you use <tt class="docutils literal"><span class="pre">Model.base_objects.values...</span></tt> as
|
||||
this is guaranteed to not change.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li><tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet supported (support will be added
|
||||
in the future).</li>
|
||||
</ul>
|
||||
@@ -537,27 +553,40 @@ class ModelA(ShowFieldType, PolymorphicModel):
|
||||
</pre>
|
||||
<p>You may also use ShowFieldContent or ShowFieldTypeAndContent to display
|
||||
additional information when printing querysets (or converting them to text).</p>
|
||||
<p>When showing field contents, they will be truncated to 20 characters. You can
|
||||
modify this behaviour by setting a class variable like this:</p>
|
||||
<pre class="literal-block">
|
||||
class ModelA(ShowFieldType, PolymorphicModel):
|
||||
polymorphic_showfield_max_field_width = 20
|
||||
...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="custom-managers-querysets-manager-inheritance">
|
||||
<h1><a class="toc-backref" href="#id5">Custom Managers, Querysets & Manager Inheritance</a></h1>
|
||||
<h1><a class="toc-backref" href="#id6">Custom Managers, Querysets & Manager Inheritance</a></h1>
|
||||
<div class="section" id="using-a-custom-manager">
|
||||
<h2>Using a Custom Manager</h2>
|
||||
<p>A nice feature of Django is the possibility to define one's own custom object managers.
|
||||
This is fully supported with django_polymorphic: For creating a custom polymorphic
|
||||
manager class, just derive your manager from <tt class="docutils literal">PolymorphicManager</tt> instead of
|
||||
<tt class="docutils literal">models.Manager</tt>. Just as with vanilla Django, in your model class, you should
|
||||
<tt class="docutils literal">models.Manager</tt>. As with vanilla Django, in your model class, you should
|
||||
explicitly add the default manager first, and then your custom manager:</p>
|
||||
<pre class="literal-block">
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||
|
||||
class MyOrderedManager(PolymorphicManager):
|
||||
def get_query_set(self):
|
||||
return super(MyOrderedManager,self).get_query_set().order_by('some_field')
|
||||
class TimeOrderedManager(PolymorphicManager):
|
||||
def get_query_set(self):
|
||||
qs = super(TimeOrderedManager,self).get_query_set()
|
||||
return qs.order_by('-start_date') # order the queryset
|
||||
|
||||
class MyModel(PolymorphicModel):
|
||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
||||
ordered_objects = MyOrderedManager() # then add your own manager
|
||||
def most_recent(self):
|
||||
qs = self.get_query_set() # get my ordered queryset
|
||||
return qs[:10] # limit => get ten most recent entries
|
||||
|
||||
class Project(PolymorphicModel):
|
||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
||||
objects_ordered = TimeOrderedManager() # then add your own manager
|
||||
start_date = DateTimeField() # project start is this date/time
|
||||
</pre>
|
||||
<p>The first manager defined ('objects' in the example) is used by
|
||||
Django as automatic manager for several purposes, including accessing
|
||||
@@ -568,18 +597,33 @@ the plain <tt class="docutils literal">PolymorphicManager</tt> here.</p>
|
||||
<h2>Manager Inheritance</h2>
|
||||
<p>Polymorphic models inherit/propagate all managers from their
|
||||
base models, as long as these are polymorphic. This means that all
|
||||
managers defined in polymorphic base models work just the same as if
|
||||
they were defined in the new model.</p>
|
||||
<p>An example (inheriting from MyModel above):</p>
|
||||
managers defined in polymorphic base models continue to work as
|
||||
expected in models inheriting from this base model:</p>
|
||||
<pre class="literal-block">
|
||||
class MyModel2(MyModel):
|
||||
pass
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||
|
||||
# Managers inherited from MyModel:
|
||||
# the regular 'objects' manager and the custom 'ordered_objects' manager
|
||||
>>> MyModel2.objects.all()
|
||||
>>> MyModel2.ordered_objects.all()
|
||||
class TimeOrderedManager(PolymorphicManager):
|
||||
def get_query_set(self):
|
||||
qs = super(TimeOrderedManager,self).get_query_set()
|
||||
return qs.order_by('-start_date') # order the queryset
|
||||
|
||||
def most_recent(self):
|
||||
qs = self.get_query_set() # get my ordered queryset
|
||||
return qs[:10] # limit => get ten most recent entries
|
||||
|
||||
class Project(PolymorphicModel):
|
||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
||||
objects_ordered = TimeOrderedManager() # then add your own manager
|
||||
start_date = DateTimeField() # project start is this date/time
|
||||
|
||||
class ArtProject(Project): # inherit from Project, inheriting its fields and managers
|
||||
artist = models.CharField(max_length=30)
|
||||
</pre>
|
||||
<p>ArtProject inherited the managers <tt class="docutils literal">objects</tt> and <tt class="docutils literal">objects_ordered</tt> from Project.</p>
|
||||
<p><tt class="docutils literal">ArtProject.objects_ordered.all()</tt> will return all art projects ordered
|
||||
regarding their start time and <tt class="docutils literal">ArtProject.objects_ordered.most_recent()</tt>
|
||||
will return the ten most recent art projects.
|
||||
.</p>
|
||||
</div>
|
||||
<div class="section" id="using-a-custom-queryset-class">
|
||||
<h2>Using a Custom Queryset Class</h2>
|
||||
@@ -601,10 +645,16 @@ class MyModel(PolymorphicModel):
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="performance-considerations">
|
||||
<h1><a class="toc-backref" href="#id6">Performance Considerations</a></h1>
|
||||
<p>The current implementation is pretty simple and does not use any
|
||||
<h1><a class="toc-backref" href="#id7">Performance Considerations</a></h1>
|
||||
<p>The current implementation is rather simple and does not use any
|
||||
custom SQL or Django DB layer internals - it is purely based on the
|
||||
standard Django ORM. Right now the query</p>
|
||||
standard Django ORM.</p>
|
||||
<p>The advantages are that the implementation naturally works on all
|
||||
supported database management systems, and consists of rather
|
||||
clean source code which can be easily understood and enhanced.</p>
|
||||
<p>The disadvantage is that this approach can not deliver the optimum
|
||||
performance as it introduces additional database queries.</p>
|
||||
<p>Specifically, the query:</p>
|
||||
<pre class="literal-block">
|
||||
result_objects = list( ModelA.objects.filter(...) )
|
||||
</pre>
|
||||
@@ -615,41 +665,58 @@ class <tt class="docutils literal">ModelA</tt>. If 50 objects are <tt class="doc
|
||||
two queries are executed. The pathological worst case is 101 db queries if
|
||||
result_objects contains 100 different object types (with all of them
|
||||
subclasses of <tt class="docutils literal">ModelA</tt>).</p>
|
||||
<p>Performance ist relative: when Django users create their own
|
||||
polymorphic ad-hoc solution (without a tool like <tt class="docutils literal">django_polymorphic</tt>),
|
||||
this usually results in a variation of</p>
|
||||
<p>Usually, when Django users create their own polymorphic ad-hoc solution
|
||||
without a tool like django_polymorphic, this usually results in a variation of</p>
|
||||
<pre class="literal-block">
|
||||
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
||||
</pre>
|
||||
<p>which has really bad performance. Relative to this, the
|
||||
performance of the current <tt class="docutils literal">django_polymorphic</tt> is pretty good.
|
||||
It's probably efficient enough for the majority of use cases.</p>
|
||||
<p>Chunking: The implementation always requests objects in chunks of
|
||||
size <tt class="docutils literal">Polymorphic_QuerySet_objects_per_request</tt>. This limits the
|
||||
complexity/duration for each query, including the pathological cases.</p>
|
||||
<p>which has exceptionally bad performance, as it introduces one additional
|
||||
SQL query for every object in the result which is not of class <tt class="docutils literal">BaseModel</tt>.
|
||||
Relative to this, the performance of the current django_polymorphic
|
||||
implementation is very good.</p>
|
||||
<p>If your project however needs perfect performance and the current
|
||||
performance implications of django_polymorphic are not acceptable, then
|
||||
basically there are the two options of either foregoing of an essential aspect
|
||||
of object oriented programming or optimizing django_polymorphic.</p>
|
||||
<p>Foregoing the benefits of this aspect of object oriented programming
|
||||
for projects that could benefit from it will however usually lead to bloated code,
|
||||
unnecessary complexity and considerably more of the programmer's time to
|
||||
create and update the implementation, together with the disadvantages
|
||||
of a less flexible and less future-proof solution. Throwing a little more
|
||||
hardware on the problem might be the least expensive solution in most cases.</p>
|
||||
</div>
|
||||
<div class="section" id="possible-optimizations">
|
||||
<h1><a class="toc-backref" href="#id7">Possible Optimizations</a></h1>
|
||||
<p><tt class="docutils literal">PolymorphicQuerySet</tt> can be optimized to require only one SQL query
|
||||
for the queryset evaluation and retrieval of all objects.</p>
|
||||
<p>Basically, what ist needed is a possibility to pull in the fields
|
||||
from all relevant sub-models with one SQL query. However, some deeper
|
||||
digging into the Django database layer will be required in order to
|
||||
make this happen.</p>
|
||||
<p>An optimized version might require an SQL database. For non-SQL databases
|
||||
the implementation could fall back to the current ORM-only
|
||||
implementation.</p>
|
||||
<h1><a class="toc-backref" href="#id8">Possible Optimizations</a></h1>
|
||||
<p>Django_polymorphic can be optimized to require only one
|
||||
SQL query for the queryset evaluation and retrieval of all objects.</p>
|
||||
<p>Probably all that would be needed seems support for an additional
|
||||
queryset function in Django's database layer, like:</p>
|
||||
<pre class="literal-block">
|
||||
ModelA.objects.join_models(on="field_name", models=[ModelB, ModelC])
|
||||
</pre>
|
||||
<p>or, less general but more simple:</p>
|
||||
<pre class="literal-block">
|
||||
ModelA.objects.join_tables(on="field_name", tables=['tableB','tableC'])
|
||||
</pre>
|
||||
<p>This would add additional left outer joins to the query and then add
|
||||
the resulting fields from this join to the result objects.
|
||||
E.g. a query for <tt class="docutils literal">ModelA</tt> objects would need to join the <tt class="docutils literal">ModelB</tt>
|
||||
and <tt class="docutils literal">ModelC</tt> tables on the the field <tt class="docutils literal">id</tt> and add the fields <tt class="docutils literal">field2</tt>
|
||||
and <tt class="docutils literal">field3</tt> from the joined tables to the resulting objects.</p>
|
||||
<p>An optimization like this might require an SQL database.
|
||||
For non-SQL databases the implementation could fall back to
|
||||
the current ORM-only implementation.</p>
|
||||
<div class="section" id="sql-complexity-of-an-optimized-implementation">
|
||||
<h2>SQL Complexity of an Optimized Implementation</h2>
|
||||
<p>With only one SQL query, one SQL join for each possible subclass
|
||||
would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).
|
||||
With two SQL queries, the number of joins could be reduced to the
|
||||
number of actuallly occurring subclasses in the result. A final
|
||||
implementation might want to use one query only if the number of
|
||||
possible subclasses (and therefore joins) is not too large, and
|
||||
two queries otherwise (using the first query to determine the
|
||||
actually occurring subclasses, reducing the number of joins for
|
||||
the second).</p>
|
||||
would be needed (<tt class="docutils literal">BaseModel.__subclasses__()</tt>, recursively).</p>
|
||||
<p>With two SQL queries, the number of joins could be reduced to the
|
||||
number of actuallly occurring subclasses in the specific result.</p>
|
||||
<p>A perfect implementation might want to use one query only
|
||||
if the number of possible subclasses (and therefore joins) is not
|
||||
too large, and two queries otherwise (using the first query to
|
||||
determine the actually occurring subclasses, reducing the number
|
||||
of joins for the second).</p>
|
||||
<p>The number of joins needed for polymorphic object retrieval might
|
||||
raise concerns regarding the efficiency of these database
|
||||
queries. It seems likely however, that the increased number of joins
|
||||
@@ -657,30 +724,21 @@ is no problem for the supported DBM systems in all realistic use cases.
|
||||
Should the number of joins of the more extreme use cases turn out to
|
||||
be problematic, it is possible to split any problematic query into, for example,
|
||||
two queries with only half the number of joins each.</p>
|
||||
</div>
|
||||
<div class="section" id="in-general">
|
||||
<h2>In General</h2>
|
||||
<p>Let's not forget that the above is just about optimization.
|
||||
The current implementation already works well - and perhaps well
|
||||
enough for the majority of applications.</p>
|
||||
<p>Also, it seems that further optimization (down to one DB request)
|
||||
would be restricted to a relatively small area of the code, and
|
||||
be mostly independent from the rest of the module.
|
||||
So it seems this optimization can be done at any later time
|
||||
(like when it's needed).</p>
|
||||
<p>It seems that further optimization (down to one DB request)
|
||||
of django_polymorphic would be restricted to a relatively small area of
|
||||
the code ("query.py"), and be pretty much independent from the rest of the module.
|
||||
Such an optimization can be done at any later time (like when it's needed).</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="restrictions-caveats">
|
||||
<span id="restrictions"></span><h1><a class="toc-backref" href="#id8">Restrictions & Caveats</a></h1>
|
||||
<ul class="simple">
|
||||
<li>The queryset methods <tt class="docutils literal">values()</tt>, <tt class="docutils literal">values_list()</tt>, <tt class="docutils literal">select_related()</tt>,
|
||||
<tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet fully supported (see above)</li>
|
||||
</ul>
|
||||
<span id="restrictions"></span><h1><a class="toc-backref" href="#id9">Restrictions & Caveats</a></h1>
|
||||
<ul class="simple">
|
||||
<li>Queryset methods <tt class="docutils literal">values()</tt>, <tt class="docutils literal">values_list()</tt>, <tt class="docutils literal">select_related()</tt>,
|
||||
<tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet fully supported (see above).
|
||||
<tt class="docutils literal">extra()</tt> has one restriction: the resulting objects are required to have
|
||||
a unique primary key within the result set</li>
|
||||
<li>Django Admin Integration: There currently is no specific admin integration,
|
||||
but it would most likely make sense to have one.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li>Diamond shaped inheritance: There seems to be a general problem
|
||||
with diamond shaped multiple model inheritance with Django models
|
||||
(tested with V1.1 - V1.3).
|
||||
@@ -688,19 +746,21 @@ An example is here: <a class="reference external" href="http://code.djangoprojec
|
||||
This problem is aggravated when trying to enhance models.Model
|
||||
by subclassing it instead of modifying Django core (as we do here
|
||||
with PolymorphicModel).</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li>The enhanced filter-definitions/Q-objects only work as arguments
|
||||
for the methods of the polymorphic querysets. Please see above
|
||||
for <tt class="docutils literal">translate_polymorphic_Q_object</tt>.</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
|
||||
in the base model (the base model directly inheriting from
|
||||
PolymorphicModel). You need to be aware of this when using the
|
||||
<tt class="docutils literal">dumpdata</tt> management command or any other low-level
|
||||
database operations. E.g. if you rename models or apps or copy
|
||||
objects from one database to another, then Django's ContentType
|
||||
table needs to be corrected/copied too. This is of course generally
|
||||
the case for any models using Django's ContentType.</li>
|
||||
<li>Django 1.1 only - the names of polymorphic models must be unique
|
||||
in the whole project, even if they are in two different apps.
|
||||
This results from a restriction in the Django 1.1 "related_name"
|
||||
option (fixed in Django 1.2).</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li>Django 1.1 only - when ContentType is used in models, Django's
|
||||
seralisation or fixtures cannot be used (all polymorphic models
|
||||
use ContentType). This issue seems to be resolved for Django 1.2
|
||||
@@ -710,25 +770,15 @@ use ContentType). This issue seems to be resolved for Django 1.2
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="simple">
|
||||
<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
|
||||
in the base model (the base model directly inheriting from
|
||||
PolymorphicModel). You need to be aware of this when using the
|
||||
<tt class="docutils literal">dumpdata</tt> management command or any other low-level
|
||||
database operations. E.g. if you rename models or apps or copy
|
||||
objects from one database to another, then Django's ContentType
|
||||
table needs to be corrected/copied too. This is of course generally
|
||||
the case for any models using Django's ContentType.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="project-status">
|
||||
<h1><a class="toc-backref" href="#id9">Project Status</a></h1>
|
||||
<h1><a class="toc-backref" href="#id10">Project Status</a></h1>
|
||||
<p>Django_polymorphic works well for a considerable number of users now,
|
||||
and no major problems have shown up for many months.
|
||||
The API can be considered stable beginning with this release.</p>
|
||||
The API can be considered stable beginning with the V1.0 release.</p>
|
||||
</div>
|
||||
<div class="section" id="links">
|
||||
<h1><a class="toc-backref" href="#id10">Links</a></h1>
|
||||
<h1><a class="toc-backref" href="#id11">Links</a></h1>
|
||||
<ul class="simple">
|
||||
<li><a class="reference external" href="http://code.djangoproject.com/wiki/ModelInheritance">http://code.djangoproject.com/wiki/ModelInheritance</a></li>
|
||||
<li><a class="reference external" href="http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html">http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html</a></li>
|
||||
|
||||
Reference in New Issue
Block a user