Port documentation to Sphinx, cleanup README
parent
8cf313335c
commit
c933be9c24
|
|
@ -20,4 +20,5 @@ Contributors
|
||||||
|
|
||||||
Former authors / maintainers
|
Former authors / maintainers
|
||||||
============================
|
============================
|
||||||
|
|
||||||
* Bert Constantin 2009/2010 (Original author, disappeared from the internet :( )
|
* Bert Constantin 2009/2010 (Original author, disappeared from the internet :( )
|
||||||
|
|
|
||||||
661
DOCS.rst
661
DOCS.rst
|
|
@ -1,661 +0,0 @@
|
||||||
Polymorphic Models for Django
|
|
||||||
=============================
|
|
||||||
|
|
||||||
.. contents:: Table of Contents
|
|
||||||
:depth: 1
|
|
||||||
|
|
||||||
|
|
||||||
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="Department Party")
|
|
||||||
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
|
|
||||||
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
|
||||||
|
|
||||||
Get polymorphic query results
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
>>> Project.objects.all()
|
|
||||||
[ <Project: id 1, topic "Department Party">,
|
|
||||||
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
|
||||||
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
|
||||||
|
|
||||||
use ``instance_of`` or ``not_instance_of`` for narrowing the result to specific subtypes:
|
|
||||||
|
|
||||||
>>> Project.objects.instance_of(ArtProject)
|
|
||||||
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner"> ]
|
|
||||||
|
|
||||||
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
|
|
||||||
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
|
||||||
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
|
||||||
|
|
||||||
Polymorphic filtering: Get all projects where Mr. Turner is involved as an artist
|
|
||||||
or supervisor (note the three underscores):
|
|
||||||
|
|
||||||
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
|
|
||||||
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
|
||||||
<ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
|
|
||||||
|
|
||||||
This is basically all you need to know, as django_polymorphic mostly
|
|
||||||
works fully automatic and just delivers the expected ("pythonic") results.
|
|
||||||
|
|
||||||
Note: In all example output, above and below, for a nicer and more informative
|
|
||||||
output the ``ShowFieldType`` mixin has been used (documented below).
|
|
||||||
|
|
||||||
|
|
||||||
List of Features
|
|
||||||
================
|
|
||||||
|
|
||||||
* Fully automatic - generally makes sure that the same objects are
|
|
||||||
returned from the database that were stored there, regardless how
|
|
||||||
they are retrieved
|
|
||||||
* Only on models that request polymorphic behaviour (and the
|
|
||||||
models inheriting from them)
|
|
||||||
* Full support for ForeignKeys, ManyToManyFields and OneToToneFields
|
|
||||||
* Filtering for classes, equivalent to python's isinstance():
|
|
||||||
``instance_of(...)`` and ``not_instance_of(...)``
|
|
||||||
* Polymorphic filtering/ordering etc., allowing the use of fields of
|
|
||||||
derived models ("ArtProject___artist")
|
|
||||||
* Support for user-defined custom managers
|
|
||||||
* Automatic inheritance of custom managers
|
|
||||||
* Support for user-defined custom queryset classes
|
|
||||||
* Non-polymorphic queries if needed, with no other change in
|
|
||||||
features/behaviour
|
|
||||||
* Combining querysets of different types/models ("qs3 = qs1 | qs2")
|
|
||||||
* Nice/informative display of polymorphic queryset results
|
|
||||||
|
|
||||||
|
|
||||||
More about Installation / Testing
|
|
||||||
=================================
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
Django 1.1 (or later) and Python 2.4 or later. This code has been tested
|
|
||||||
on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.
|
|
||||||
|
|
||||||
Included Test Suite
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
The repository (or tar file) contains a complete Django project
|
|
||||||
that may be used for tests or experiments, without any installation needed.
|
|
||||||
|
|
||||||
To run the included test suite, in the directory "...django_polymorphic" execute::
|
|
||||||
|
|
||||||
./manage test polymorphic
|
|
||||||
|
|
||||||
The management command ``pcmd.py`` in the app ``pexp`` can be used
|
|
||||||
for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
|
|
||||||
to your liking, then run::
|
|
||||||
|
|
||||||
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
|
||||||
./manage pcmd
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
In the directory "...django_polymorphic", execute ``sudo python setup.py install``.
|
|
||||||
|
|
||||||
Alternatively you can simply copy the ``polymorphic`` subdirectory
|
|
||||||
(under "django_polymorphic") into your Django project dir
|
|
||||||
(e.g. if you want to distribute your project with more 'batteries included').
|
|
||||||
|
|
||||||
If you want to run the test cases in `polymorphic/tests.py`, you need to add
|
|
||||||
``polymorphic`` to your INSTALLED_APPS setting.
|
|
||||||
|
|
||||||
Django's ContentType framework (``django.contrib.contenttypes``)
|
|
||||||
needs to be listed in INSTALLED_APPS (usually it already is).
|
|
||||||
|
|
||||||
|
|
||||||
More Polymorphic Functionality
|
|
||||||
==============================
|
|
||||||
|
|
||||||
In the examples below, these models are being used::
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
Using polymorphic models in the admin interface
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
Naturally, it's possible to register individual polymorphic models in the Django admin interface.
|
|
||||||
However, to use these models in a single cohesive interface, some extra base classes are available.
|
|
||||||
|
|
||||||
The polymorphic admin interface works in a simple way:
|
|
||||||
|
|
||||||
* The add screen gains an additional step where the desired child model is selected.
|
|
||||||
* The edit screen displays the admin interface of the child model.
|
|
||||||
* The list screen still displays all objects of the base class.
|
|
||||||
|
|
||||||
The polymorphic admin is implemented via a parent admin that forwards the *edit* and *delete* views
|
|
||||||
to the ``ModelAdmin`` of the derived child model. The *list* page is still implemented by the parent model admin.
|
|
||||||
|
|
||||||
Both the parent model and child model need to have a ``ModelAdmin`` class.
|
|
||||||
Only the ``ModelAdmin`` class of the parent/base model has to be registered in the Django admin site.
|
|
||||||
|
|
||||||
The parent model
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The parent model needs to inherit ``PolymorphicParentModelAdmin``, and implement the following:
|
|
||||||
|
|
||||||
* ``base_model`` should be set
|
|
||||||
* ``child_models`` should be set, or:
|
|
||||||
|
|
||||||
* ``get_child_models()`` should return a list with (Model, ModelAdmin) tuple.
|
|
||||||
|
|
||||||
The exact implementation can depend on the way your module is structured.
|
|
||||||
For simple inheritance situations, ``child_models`` is best suited.
|
|
||||||
For large applications, this leaves room for a plugin registration system.
|
|
||||||
|
|
||||||
The child models
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The admin interface of the derived models should inherit from ``PolymorphicChildModelAdmin``.
|
|
||||||
Again, ``base_model`` should be set in this class as well.
|
|
||||||
This class implements the following features:
|
|
||||||
|
|
||||||
* It corrects the breadcrumbs in the admin pages.
|
|
||||||
* It extends the template lookup paths, to look for both the parent model and child model in the ``admin/app/model/change_form.html`` path.
|
|
||||||
* It allows to set ``base_form`` so the derived class will automatically include other fields in the form.
|
|
||||||
* It allows to set ``base_fieldsets`` so the derived class will automatically display any extra fields.
|
|
||||||
|
|
||||||
By adding ``polymorphic`` to the ``INSTALLED_APPS``, the breadcrumbs will be
|
|
||||||
fixed as well, to stay the same for all child models.
|
|
||||||
|
|
||||||
The standard ``ModelAdmin`` attributes ``form`` and ``fieldsets`` should rather be avoided at the base class,
|
|
||||||
because it will hide any additional fields which are defined in the derived model. Instead,
|
|
||||||
use the ``base_form`` and ``base_fieldsets`` instead. The ``PolymorphicChildModelAdmin`` will
|
|
||||||
automatically detect the additional fields that the child model has, display those in a separate fieldset.
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
from django.contrib import admin
|
|
||||||
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
|
|
||||||
|
|
||||||
|
|
||||||
class ModelAChildAdmin(PolymorphicChildModelAdmin):
|
|
||||||
""" Base admin class for all child models """
|
|
||||||
base_model = ModelA
|
|
||||||
|
|
||||||
# By using these `base_...` attributes instead of the regular ModelAdmin `form` and `fieldsets`,
|
|
||||||
# the additional fields of the child models are automatically added to the admin form.
|
|
||||||
base_form = ...
|
|
||||||
base_fieldsets = (
|
|
||||||
...
|
|
||||||
)
|
|
||||||
|
|
||||||
class ModelBAdmin(ModelAChildAdmin):
|
|
||||||
# define custom features here
|
|
||||||
|
|
||||||
class ModelCAdmin(ModelBAdmin):
|
|
||||||
# define custom features here
|
|
||||||
|
|
||||||
|
|
||||||
class ModelAParentAdmin(PolymorphicParentModelAdmin):
|
|
||||||
""" The parent model admin """
|
|
||||||
base_model = ModelA
|
|
||||||
child_models = (
|
|
||||||
(ModelB, ModelBAdmin),
|
|
||||||
(ModelC, ModelCAdmin),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Only the parent needs to be registered:
|
|
||||||
admin.site.register(ModelA, ModelAParentAdmin)
|
|
||||||
|
|
||||||
|
|
||||||
Filtering for classes (equivalent to python's isinstance() ):
|
|
||||||
-------------------------------------------------------------
|
|
||||||
|
|
||||||
>>> ModelA.objects.instance_of(ModelB)
|
|
||||||
.
|
|
||||||
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
|
||||||
|
|
||||||
In general, including or excluding parts of the inheritance tree::
|
|
||||||
|
|
||||||
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)
|
|
||||||
-----------------------------------------------------
|
|
||||||
|
|
||||||
For example, cherrypicking objects from multiple derived classes
|
|
||||||
anywhere in the inheritance tree, using Q objects (with the
|
|
||||||
syntax: ``exact model name + three _ + field name``):
|
|
||||||
|
|
||||||
>>> ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
|
|
||||||
.
|
|
||||||
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
|
||||||
|
|
||||||
|
|
||||||
Combining Querysets
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Querysets could now be regarded as object containers that allow the
|
|
||||||
aggregation of different object types, very similar to python
|
|
||||||
lists - as long as the objects are accessed through the manager of
|
|
||||||
a common base class:
|
|
||||||
|
|
||||||
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
|
|
||||||
.
|
|
||||||
[ <ModelX: id 1, field_x (CharField)>,
|
|
||||||
<ModelY: id 2, field_y (CharField)> ]
|
|
||||||
|
|
||||||
|
|
||||||
ManyToManyField, ForeignKey, OneToOneField
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
E.g., if in your model you define::
|
|
||||||
|
|
||||||
field1 = OneToOneField(ModelA)
|
|
||||||
|
|
||||||
then field1 may now also refer to objects of type ``ModelB`` or ``ModelC``.
|
|
||||||
|
|
||||||
A ManyToManyField example::
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
>>> o=RelatingModel.objects.create()
|
|
||||||
>>> o.many2many.add(ModelA.objects.get(id=1))
|
|
||||||
>>> o.many2many.add(ModelB.objects.get(id=2))
|
|
||||||
>>> o.many2many.add(ModelC.objects.get(id=3))
|
|
||||||
|
|
||||||
>>> o.many2many.all()
|
|
||||||
[ <ModelA: id 1, field1 (CharField)>,
|
|
||||||
<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 MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
|
|
||||||
pass # or add fields
|
|
||||||
|
|
||||||
Or instead integrating the third party model anywhere into an
|
|
||||||
existing polymorphic inheritance tree::
|
|
||||||
|
|
||||||
class MyBaseModel(SomePolymorphicModel):
|
|
||||||
my_field = models.CharField(max_length=10)
|
|
||||||
|
|
||||||
class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
|
|
||||||
pass # or add fields
|
|
||||||
|
|
||||||
|
|
||||||
Non-Polymorphic Queries
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
If you insert ``.non_polymorphic()`` 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
|
|
||||||
(``ModelA`` in this example).
|
|
||||||
|
|
||||||
>>> qs=ModelA.objects.non_polymorphic().all()
|
|
||||||
>>> qs
|
|
||||||
[ <ModelA: id 1, field1 (CharField)>,
|
|
||||||
<ModelA: id 2, field1 (CharField)>,
|
|
||||||
<ModelA: id 3, field1 (CharField)> ]
|
|
||||||
|
|
||||||
There are no other changes in the behaviour of the queryset. For example,
|
|
||||||
enhancements for ``filter()`` or ``instance_of()`` etc. still work as expected.
|
|
||||||
If you do the final step yourself, you get the usual polymorphic result:
|
|
||||||
|
|
||||||
>>> ModelA.objects.get_real_instances(qs)
|
|
||||||
[ <ModelA: id 1, field1 (CharField)>,
|
|
||||||
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
|
||||||
|
|
||||||
|
|
||||||
About Queryset Methods
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
* ``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
|
|
||||||
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()`` works as expected (it returns polymorphic results) but
|
|
||||||
currently has one restriction: The resulting objects are required to have
|
|
||||||
a unique primary key within the result set - otherwise an error is thrown
|
|
||||||
(this case could be made to work, however it may be mostly unneeded)..
|
|
||||||
The keyword-argument "polymorphic" is no longer supported.
|
|
||||||
You can get back the old non-polymorphic behaviour (before V1.0)
|
|
||||||
by using ``ModelA.objects.non_polymorphic().extra(...)``.
|
|
||||||
|
|
||||||
* ``get_real_instances()`` allows you to turn a
|
|
||||||
queryset or list of base model objects efficiently into the real objects.
|
|
||||||
For example, you could do ``base_objects_queryset=ModelA.extra(...).non_polymorphic()``
|
|
||||||
and then call ``real_objects=base_objects_queryset.get_real_instances()``.Or alternatively
|
|
||||||
.``real_objects=ModelA.objects..get_real_instances(base_objects_queryset_or_object_list)``
|
|
||||||
|
|
||||||
* ``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.
|
|
||||||
|
|
||||||
* ``defer()`` and ``only()`` are not yet supported (support will be added
|
|
||||||
in the future).
|
|
||||||
|
|
||||||
|
|
||||||
Using enhanced Q-objects in any Places
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
The queryset enhancements (e.g. ``instance_of``) only work as arguments
|
|
||||||
to the member functions of a polymorphic queryset. Occationally it may
|
|
||||||
be useful to be able to use Q objects with these enhancements in other places.
|
|
||||||
As Django doesn't understand these enhanced Q objects, you need to
|
|
||||||
transform them manually into normal Q objects before you can feed them
|
|
||||||
to a Django queryset or function::
|
|
||||||
|
|
||||||
normal_q_object = ModelA.translate_polymorphic_Q_object( Q(instance_of=Model2B) )
|
|
||||||
|
|
||||||
This function cannot be used at model creation time however (in models.py),
|
|
||||||
as it may need to access the ContentTypes database table.
|
|
||||||
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
When showing field contents, they will be truncated to 20 characters. You can
|
|
||||||
modify this behaviour by setting a class variable in your model like this::
|
|
||||||
|
|
||||||
class ModelA(ShowFieldType, PolymorphicModel):
|
|
||||||
polymorphic_showfield_max_field_width = 20
|
|
||||||
...
|
|
||||||
|
|
||||||
Similarly, pre-V1.0 output formatting can be re-estated by using
|
|
||||||
``polymorphic_showfield_old_format = True``.
|
|
||||||
|
|
||||||
Custom Managers, Querysets & Manager Inheritance
|
|
||||||
================================================
|
|
||||||
|
|
||||||
Using a Custom Manager
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
A nice feature of Django is the possibility to define one's own custom object managers.
|
|
||||||
This is fully supported with django_polymorphic: For creating a custom polymorphic
|
|
||||||
manager class, just derive your manager from ``PolymorphicManager`` instead of
|
|
||||||
``models.Manager``. 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
|
|
||||||
|
|
||||||
class TimeOrderedManager(PolymorphicManager):
|
|
||||||
def get_query_set(self):
|
|
||||||
qs = super(TimeOrderedManager,self).get_query_set()
|
|
||||||
return qs.order_by('-start_date') # order the queryset
|
|
||||||
|
|
||||||
def most_recent(self):
|
|
||||||
qs = self.get_query_set() # get my ordered queryset
|
|
||||||
return qs[:10] # limit => get ten most recent entries
|
|
||||||
|
|
||||||
class Project(PolymorphicModel):
|
|
||||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
|
||||||
objects_ordered = TimeOrderedManager() # then add your own manager
|
|
||||||
start_date = DateTimeField() # project start is this date/time
|
|
||||||
|
|
||||||
The first manager defined ('objects' in the example) is used by
|
|
||||||
Django as automatic manager for several purposes, including accessing
|
|
||||||
related objects. It must not filter objects and it's safest to use
|
|
||||||
the plain ``PolymorphicManager`` here.
|
|
||||||
|
|
||||||
Manager Inheritance
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Polymorphic models inherit/propagate all managers from their
|
|
||||||
base models, as long as these are polymorphic. This means that all
|
|
||||||
managers defined in polymorphic base models continue to work as
|
|
||||||
expected in models inheriting from this base model::
|
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
|
||||||
|
|
||||||
class TimeOrderedManager(PolymorphicManager):
|
|
||||||
def get_query_set(self):
|
|
||||||
qs = super(TimeOrderedManager,self).get_query_set()
|
|
||||||
return qs.order_by('-start_date') # order the queryset
|
|
||||||
|
|
||||||
def most_recent(self):
|
|
||||||
qs = self.get_query_set() # get my ordered queryset
|
|
||||||
return qs[:10] # limit => get ten most recent entries
|
|
||||||
|
|
||||||
class Project(PolymorphicModel):
|
|
||||||
objects = PolymorphicManager() # add the default polymorphic manager first
|
|
||||||
objects_ordered = TimeOrderedManager() # then add your own manager
|
|
||||||
start_date = DateTimeField() # project start is this date/time
|
|
||||||
|
|
||||||
class ArtProject(Project): # inherit from Project, inheriting its fields and managers
|
|
||||||
artist = models.CharField(max_length=30)
|
|
||||||
|
|
||||||
ArtProject inherited the managers ``objects`` and ``objects_ordered`` from Project.
|
|
||||||
|
|
||||||
``ArtProject.objects_ordered.all()`` will return all art projects ordered
|
|
||||||
regarding their start time and ``ArtProject.objects_ordered.most_recent()``
|
|
||||||
will return the ten most recent art projects.
|
|
||||||
.
|
|
||||||
|
|
||||||
Using a Custom Queryset Class
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
The ``PolymorphicManager`` 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::
|
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
|
||||||
|
|
||||||
class MyQuerySet(PolymorphicQuerySet):
|
|
||||||
def my_queryset_method(...):
|
|
||||||
...
|
|
||||||
|
|
||||||
class MyModel(PolymorphicModel):
|
|
||||||
my_objects=PolymorphicManager(MyQuerySet)
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
Performance Considerations
|
|
||||||
==========================
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Specifically, 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 SQL query if all are
|
|
||||||
class ``ModelA``. If 50 objects are ``ModelA`` and 50 are ``ModelB``, 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 ``ModelA``).
|
|
||||||
|
|
||||||
Usually, when Django users create their own polymorphic ad-hoc solution
|
|
||||||
without a tool like django_polymorphic, this usually results in a variation of ::
|
|
||||||
|
|
||||||
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
|
||||||
|
|
||||||
which has very bad performance, as it introduces one additional
|
|
||||||
SQL query for every object in the result which is not of class ``BaseModel``.
|
|
||||||
|
|
||||||
Compared to these solutions, django_polymorphic has the advantage
|
|
||||||
that it only needs one sql request per *object type*, and not *per object*.
|
|
||||||
|
|
||||||
.. _performance:
|
|
||||||
|
|
||||||
Performance Problems with PostgreSQL, MySQL and SQLite3
|
|
||||||
-------------------------------------------------------
|
|
||||||
|
|
||||||
Current relational DBM systems seem to have general problems with
|
|
||||||
the SQL queries produced by object relational mappers like the Django
|
|
||||||
ORM, if these use multi-table inheritance like Django's ORM does.
|
|
||||||
The "inner joins" in these queries can perform very badly.
|
|
||||||
This is independent of django_polymorphic and affects all uses of
|
|
||||||
multi table Model inheritance.
|
|
||||||
|
|
||||||
Concrete benchmark results are forthcoming (please see discussion forum).
|
|
||||||
|
|
||||||
Please also see this `post (and comments) from Jacob Kaplan-Moss`_.
|
|
||||||
|
|
||||||
.. _post (and comments) from Jacob Kaplan-Moss: http://www.jacobian.org/writing/concrete-inheritance/
|
|
||||||
|
|
||||||
|
|
||||||
.. _restrictions:
|
|
||||||
|
|
||||||
Restrictions & Caveats
|
|
||||||
======================
|
|
||||||
|
|
||||||
* Database Performance regarding concrete Model inheritance in general.
|
|
||||||
Please see "Performance Problems" above.
|
|
||||||
|
|
||||||
* Queryset methods ``values()``, ``values_list()``, ``select_related()``,
|
|
||||||
``defer()`` and ``only()`` are not yet fully supported (see above).
|
|
||||||
``extra()`` has one restriction: the resulting objects are required to have
|
|
||||||
a unique primary key within the result set.
|
|
||||||
|
|
||||||
* 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``.
|
|
||||||
|
|
||||||
* A reference (``ContentType``) 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
|
|
||||||
``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.
|
|
||||||
|
|
||||||
* 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 (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).
|
|
||||||
|
|
||||||
+ http://code.djangoproject.com/ticket/7052
|
|
||||||
+ http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django
|
|
||||||
|
|
||||||
|
|
||||||
Project Status
|
|
||||||
==============
|
|
||||||
|
|
||||||
Django_polymorphic works well for a considerable number of users now,
|
|
||||||
and no major problems have shown up for many months.
|
|
||||||
The API can be considered stable beginning with the V1.0 release.
|
|
||||||
|
|
||||||
|
|
||||||
Links
|
|
||||||
=====
|
|
||||||
|
|
||||||
- http://code.djangoproject.com/wiki/ModelInheritance
|
|
||||||
- http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html
|
|
||||||
- http://www.djangosnippets.org/snippets/1031/
|
|
||||||
- http://www.djangosnippets.org/snippets/1034/
|
|
||||||
- http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&q=model+inheritance+CORBA#a20fabc661b7035d
|
|
||||||
- http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&q=inheritance#0b92971ffc0aa6f8
|
|
||||||
- http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&q=inheritance#d8c0af3dacad412d
|
|
||||||
- http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f
|
|
||||||
- http://peterbraden.co.uk/article/django-inheritance
|
|
||||||
- http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django
|
|
||||||
- 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/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses
|
|
||||||
- http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&q=polymorphic#e676a537d735d9ef
|
|
||||||
- http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&q=model+inheritance#bc18c18b2e83881e
|
|
||||||
- http://code.djangoproject.com/ticket/10808
|
|
||||||
- http://code.djangoproject.com/ticket/7270
|
|
||||||
|
|
||||||
218
README.rst
218
README.rst
|
|
@ -1,36 +1,11 @@
|
||||||
Polymorphic Models for Django
|
Polymorphic Models for Django
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
|
|
||||||
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, patch or fork (GitHub_, Bitbucket_, Group_, Mail_)
|
|
||||||
|
|
||||||
.. _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/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/django_polymorphic
|
|
||||||
.. _Changelog: http://bserve.webhop.org/django_polymorphic/CHANGES.html
|
|
||||||
|
|
||||||
.. _good-for:
|
|
||||||
|
|
||||||
What is django_polymorphic good for?
|
What is django_polymorphic good for?
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
Let's assume the models ``ArtProject`` and ``ResearchProject`` are derived
|
Let's assume the models ``ArtProject`` and ``ResearchProject`` are derived
|
||||||
from the model ``Project``, and let's store one of each into the database:
|
from the model ``Project``, and stored in the database:
|
||||||
|
|
||||||
>>> Project.objects.create(topic="Department Party")
|
>>> Project.objects.create(topic="Department Party")
|
||||||
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
|
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
|
||||||
|
|
@ -40,192 +15,43 @@ If we want to retrieve all our projects, we do:
|
||||||
|
|
||||||
>>> Project.objects.all()
|
>>> Project.objects.all()
|
||||||
|
|
||||||
Using django_polymorphic, we simply get what we stored::
|
Using *django-polymorphic*, we simply get what we stored::
|
||||||
|
|
||||||
[ <Project: id 1, topic "Department Party">,
|
[ <Project: id 1, topic "Department Party">,
|
||||||
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
|
|
||||||
Using vanilla Django, we get incomplete objects, which is probably not what we wanted::
|
Using vanilla Django, we get the base class objects, which is probably not what we wanted::
|
||||||
|
|
||||||
[ <Project: id 1, topic "Department Party">,
|
[ <Project: id 1, topic "Department Party">,
|
||||||
<Project: id 2, topic "Painting with Tim">,
|
<Project: id 2, topic "Painting with Tim">,
|
||||||
<Project: id 3, topic "Swallow Aerodynamics"> ]
|
<Project: id 3, topic "Swallow Aerodynamics"> ]
|
||||||
|
|
||||||
It's very similar for ForeignKeys, ManyToManyFields or OneToOneFields.
|
This also works when the polymorphic model is accessed via
|
||||||
|
ForeignKeys, ManyToManyFields or OneToOneFields.
|
||||||
|
|
||||||
In general, the effect of django_polymorphic is twofold:
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
On one hand it makes sure that model inheritance just works as you
|
* Full admin integation.
|
||||||
expect, by simply ensuring that you always get back exactly the same
|
* ORM integration:
|
||||||
objects from the database you stored there - regardless how you access
|
|
||||||
them, making model inheritance much more "pythonic".
|
|
||||||
This can save you a lot of unpleasant workarounds that tend to
|
|
||||||
make your code messy, error-prone, and slow.
|
|
||||||
|
|
||||||
On the other hand, together with some small API additions to the Django
|
* support for ForeignKey, ManyToManyField, OneToOneField descriptors.
|
||||||
ORM, django_polymorphic enables a much more expressive and intuitive
|
* Filtering/ordering of derived models (``ArtProject___artist``).
|
||||||
programming style and also very advanced object oriented designs
|
* Filtering model types: ``instance_of(...)`` and ``not_instance_of(...)``
|
||||||
that are not possible with vanilla Django.
|
* Combining querysets of different models (``qs3 = qs1 | qs2``)
|
||||||
|
* Support for custom user-defined managers.
|
||||||
|
|
||||||
Fortunately, most of the heavy duty machinery that is needed for this
|
* Uses the minumum amount of queries needed to fetch the derived models.
|
||||||
functionality is already present in the original Django database layer.
|
* Disabling polymorphic behavior when needed.
|
||||||
Django_polymorphic adds a rather thin layer above that in order
|
|
||||||
to make real OO fully automatic and very easy to use.
|
|
||||||
|
|
||||||
There is a catch however, which applies to concrete model inheritance
|
|
||||||
in general: Current DBM systems like PostgreSQL or MySQL are not very
|
|
||||||
good at processing the required sql queries and can be rather slow in
|
|
||||||
many cases. Concrete benchmarks are forthcoming (please see
|
|
||||||
discussion forum).
|
|
||||||
|
|
||||||
For more information, please look at `Quickstart`_ or at the complete
|
|
||||||
`Installation and Usage Docs`_ and also see the `restrictions and caveats`_.
|
|
||||||
|
|
||||||
.. _restrictions and caveats: http://bserve.webhop.org/django_polymorphic/DOCS.html#restrictions
|
|
||||||
|
|
||||||
|
|
||||||
This is a V1.0 Beta/Testing Release
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
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 or have suggestions regarding the API or the
|
|
||||||
changes in this beta, please post them in the `discussion group`_
|
|
||||||
or open an issue on GitHub_ or BitBucket_ (or send me an email).
|
|
||||||
|
|
||||||
.. _discussion group: http://groups.google.de/group/django-polymorphic/topics
|
|
||||||
|
|
||||||
|
While *django-polymorphic* makes subclassed models easy to use in Django,
|
||||||
|
we still encourage to use them with caution. Each subclassed model will require
|
||||||
|
Django to perform an ``INNER JOIN`` to fetch the model fields from the database.
|
||||||
|
While taking this in mind, there are valid reasons for using subclassed models.
|
||||||
|
That's what this library is designed for!
|
||||||
|
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Django_polymorphic uses the same license as Django (BSD-like).
|
Django-polymorphic uses the same license as Django (BSD-like).
|
||||||
|
|
||||||
|
|
||||||
API Changes & Additions
|
|
||||||
=======================
|
|
||||||
|
|
||||||
|
|
||||||
November 11 2010, V1.0 API Changes
|
|
||||||
-------------------------------------------------------------------
|
|
||||||
|
|
||||||
extra() queryset method
|
|
||||||
+++++++++++++++++++++++
|
|
||||||
|
|
||||||
``.extra()`` has been re-implemented. Now it's polymorphic by
|
|
||||||
default and works (nearly) without restrictions (please see docs). This is a (very)
|
|
||||||
incompatible API change regarding previous versions of django_polymorphic.
|
|
||||||
Support for the ``polymorphic`` keyword parameter has been removed.
|
|
||||||
You can get back the non-polymorphic behaviour by using
|
|
||||||
``ModelA.objects.non_polymorphic().extra()``.
|
|
||||||
|
|
||||||
No Pretty-Printing of Querysets by default
|
|
||||||
++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
In order to improve compatibility with vanilla Django, printing quersets
|
|
||||||
(__repr__ and __unicode__) 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)
|
|
||||||
|
|
||||||
Pretty-Printing Output Format Changed
|
|
||||||
+++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
``ShowFieldContent`` and ``ShowFieldTypeAndContent`` now
|
|
||||||
use a slightly different output format. If this causes too much trouble for
|
|
||||||
your test cases, you can get the old behaviour back (mostly) by adding
|
|
||||||
``polymorphic_showfield_old_format = True`` to your model definitions.
|
|
||||||
``ShowField...`` now also produces more informative output for custom
|
|
||||||
primary keys.
|
|
||||||
|
|
||||||
polymorphic_dumpdata
|
|
||||||
++++++++++++++++++++
|
|
||||||
|
|
||||||
The ``polymorphic_dumpdata`` management command is not needed anymore
|
|
||||||
and has been disabled, as the regular Django dumpdata command now automatically
|
|
||||||
works correctly with polymorphic models (for all supported versions of Django).
|
|
||||||
|
|
||||||
Running the Test suite with Django 1.3
|
|
||||||
++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
Django 1.3 requires ``python manage.py test polymorphic`` instead of
|
|
||||||
just ``python manage.py test``.
|
|
||||||
|
|
||||||
|
|
||||||
November 01 2010, V1.0 API Additions
|
|
||||||
-------------------------------------------------------------------
|
|
||||||
|
|
||||||
* ``.non_polymorphic()`` queryset member function added. This is preferable to
|
|
||||||
using ``.base_objects...``, as it just makes the resulting queryset non-polymorphic
|
|
||||||
and does not change anything else in the behaviour of the manager used (while
|
|
||||||
``.base_objects`` is just a different manager).
|
|
||||||
|
|
||||||
* ``.get_real_instances()`` has been elevated to an official part of the API.
|
|
||||||
It allows you to turn a queryset or list of base objects into a list of the real instances.
|
|
||||||
This is useful if e.g. you use ``ModelA.objects.non_polymorphic().extra(...)`` and then want to
|
|
||||||
transform the result to its polymorphic equivalent:
|
|
||||||
|
|
||||||
>>> qs = ModelA.objects.all().non_polymorphic()
|
|
||||||
>>> real_objects = qs.get_real_instances()
|
|
||||||
|
|
||||||
is equivalent to:
|
|
||||||
|
|
||||||
>>> real_objects = ModelA.objects.all()
|
|
||||||
|
|
||||||
Instead of ``qs.get_real_instances()``, ``ModelA.objects.get_real_instances(qs)`` may be used
|
|
||||||
as well. In the latter case, ``qs`` may be any list of objects of type ModelA.
|
|
||||||
|
|
||||||
* ``translate_polymorphic_Q_object`` (see DOCS)
|
|
||||||
|
|
||||||
|
|
||||||
February 22 2010, Installation Note
|
|
||||||
-------------------------------------------------------------------
|
|
||||||
|
|
||||||
The django_polymorphic source code has been restructured
|
|
||||||
and as a result needs to be installed like a normal Django App
|
|
||||||
- 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, ...
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
.. _commit-log: http://github.com/bconstantin/django_polymorphic/commit/c2b420aea06637966a208329ef7ec853889fa4c7
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
# Makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
PAPER =
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Internal variables.
|
||||||
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
|
PAPEROPT_letter = -D latex_paper_size=letter
|
||||||
|
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
# the i18n builder cannot share the environment and doctrees with the others
|
||||||
|
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
|
||||||
|
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
|
@echo " html to make standalone HTML files"
|
||||||
|
@echo " dirhtml to make HTML files named index.html in directories"
|
||||||
|
@echo " singlehtml to make a single large HTML file"
|
||||||
|
@echo " pickle to make pickle files"
|
||||||
|
@echo " json to make JSON files"
|
||||||
|
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||||
|
@echo " qthelp to make HTML files and a qthelp project"
|
||||||
|
@echo " devhelp to make HTML files and a Devhelp project"
|
||||||
|
@echo " epub to make an epub"
|
||||||
|
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||||
|
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||||
|
@echo " text to make text files"
|
||||||
|
@echo " man to make manual pages"
|
||||||
|
@echo " texinfo to make Texinfo files"
|
||||||
|
@echo " info to make Texinfo files and run them through makeinfo"
|
||||||
|
@echo " gettext to make PO message catalogs"
|
||||||
|
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||||
|
@echo " linkcheck to check all external links for integrity"
|
||||||
|
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -rf $(BUILDDIR)/*
|
||||||
|
|
||||||
|
html:
|
||||||
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||||
|
|
||||||
|
dirhtml:
|
||||||
|
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||||
|
|
||||||
|
singlehtml:
|
||||||
|
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||||
|
|
||||||
|
pickle:
|
||||||
|
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the pickle files."
|
||||||
|
|
||||||
|
json:
|
||||||
|
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the JSON files."
|
||||||
|
|
||||||
|
htmlhelp:
|
||||||
|
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||||
|
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||||
|
|
||||||
|
qthelp:
|
||||||
|
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||||
|
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||||
|
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-polymorphic.qhcp"
|
||||||
|
@echo "To view the help file:"
|
||||||
|
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-polymorphic.qhc"
|
||||||
|
|
||||||
|
devhelp:
|
||||||
|
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished."
|
||||||
|
@echo "To view the help file:"
|
||||||
|
@echo "# mkdir -p $$HOME/.local/share/devhelp/django-polymorphic"
|
||||||
|
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-polymorphic"
|
||||||
|
@echo "# devhelp"
|
||||||
|
|
||||||
|
epub:
|
||||||
|
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||||
|
|
||||||
|
latex:
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||||
|
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||||
|
"(use \`make latexpdf' here to do that automatically)."
|
||||||
|
|
||||||
|
latexpdf:
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||||
|
@echo "Running LaTeX files through pdflatex..."
|
||||||
|
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||||
|
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||||
|
|
||||||
|
text:
|
||||||
|
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||||
|
|
||||||
|
man:
|
||||||
|
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||||
|
|
||||||
|
texinfo:
|
||||||
|
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||||
|
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||||
|
"(use \`make info' here to do that automatically)."
|
||||||
|
|
||||||
|
info:
|
||||||
|
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||||
|
@echo "Running Texinfo files through makeinfo..."
|
||||||
|
make -C $(BUILDDIR)/texinfo info
|
||||||
|
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||||
|
|
||||||
|
gettext:
|
||||||
|
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||||
|
|
||||||
|
changes:
|
||||||
|
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||||
|
@echo
|
||||||
|
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||||
|
|
||||||
|
linkcheck:
|
||||||
|
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||||
|
@echo
|
||||||
|
@echo "Link check complete; look for any errors in the above output " \
|
||||||
|
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||||
|
|
||||||
|
doctest:
|
||||||
|
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||||
|
@echo "Testing of doctests in the sources finished, look at the " \
|
||||||
|
"results in $(BUILDDIR)/doctest/output.txt."
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
Django admin integration
|
||||||
|
========================
|
||||||
|
|
||||||
|
Off course, it's possible to register individual polymorphic models in the Django admin interface.
|
||||||
|
However, to use these models in a single cohesive interface, some extra base classes are available.
|
||||||
|
|
||||||
|
The polymorphic admin interface works in a simple way:
|
||||||
|
|
||||||
|
* The add screen gains an additional step where the desired child model is selected.
|
||||||
|
* The edit screen displays the admin interface of the child model.
|
||||||
|
* The list screen still displays all objects of the base class.
|
||||||
|
|
||||||
|
The polymorphic admin is implemented via a parent admin that forwards the *edit* and *delete* views
|
||||||
|
to the ``ModelAdmin`` of the derived child model. The *list* page is still implemented by the parent model admin.
|
||||||
|
|
||||||
|
Both the parent model and child model need to have a ``ModelAdmin`` class.
|
||||||
|
Only the ``ModelAdmin`` class of the parent/base model has to be registered in the Django admin site.
|
||||||
|
|
||||||
|
The parent model
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The parent model needs to inherit ``PolymorphicParentModelAdmin``, and implement the following:
|
||||||
|
|
||||||
|
* ``base_model`` should be set
|
||||||
|
* ``child_models`` or ``get_child_models()`` should return a list with (Model, ModelAdmin) tuple.
|
||||||
|
|
||||||
|
The exact implementation can depend on the way your module is structured.
|
||||||
|
For simple inheritance situations, ``child_models`` is the best solution.
|
||||||
|
For large applications, ``get_child_models()`` can be used to query a plugin registration system.
|
||||||
|
|
||||||
|
The child models
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The admin interface of the derived models should inherit from ``PolymorphicChildModelAdmin``.
|
||||||
|
Again, ``base_model`` should be set in this class as well.
|
||||||
|
This class implements the following features:
|
||||||
|
|
||||||
|
* It corrects the breadcrumbs in the admin pages.
|
||||||
|
* It extends the template lookup paths, to look for both the parent model and child model in the ``admin/app/model/change_form.html`` path.
|
||||||
|
* It allows to set ``base_form`` so the derived class will automatically include other fields in the form.
|
||||||
|
* It allows to set ``base_fieldsets`` so the derived class will automatically display any extra fields.
|
||||||
|
|
||||||
|
The standard ``ModelAdmin`` attributes ``form`` and ``fieldsets`` should rather be avoided at the base class,
|
||||||
|
because it will hide any additional fields which are defined in the derived model. Instead,
|
||||||
|
use the ``base_form`` and ``base_fieldsets`` instead. The ``PolymorphicChildModelAdmin`` will
|
||||||
|
automatically detect the additional fields that the child model has, display those in a separate fieldset.
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
|
||||||
|
|
||||||
|
|
||||||
|
class ModelAChildAdmin(PolymorphicChildModelAdmin):
|
||||||
|
""" Base admin class for all child models """
|
||||||
|
base_model = ModelA
|
||||||
|
|
||||||
|
# By using these `base_...` attributes instead of the regular ModelAdmin `form` and `fieldsets`,
|
||||||
|
# the additional fields of the child models are automatically added to the admin form.
|
||||||
|
base_form = ...
|
||||||
|
base_fieldsets = (
|
||||||
|
...
|
||||||
|
)
|
||||||
|
|
||||||
|
class ModelBAdmin(ModelAChildAdmin):
|
||||||
|
# define custom features here
|
||||||
|
|
||||||
|
class ModelCAdmin(ModelBAdmin):
|
||||||
|
# define custom features here
|
||||||
|
|
||||||
|
|
||||||
|
class ModelAParentAdmin(PolymorphicParentModelAdmin):
|
||||||
|
""" The parent model admin """
|
||||||
|
base_model = ModelA
|
||||||
|
child_models = (
|
||||||
|
(ModelB, ModelBAdmin),
|
||||||
|
(ModelC, ModelCAdmin),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Only the parent needs to be registered:
|
||||||
|
admin.site.register(ModelA, ModelAParentAdmin)
|
||||||
|
|
@ -0,0 +1,272 @@
|
||||||
|
Advanced features
|
||||||
|
=================
|
||||||
|
|
||||||
|
In the examples below, these models are being used::
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
Filtering for classes (equivalent to python's isinstance() ):
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
>>> ModelA.objects.instance_of(ModelB)
|
||||||
|
.
|
||||||
|
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
|
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
|
In general, including or excluding parts of the inheritance tree::
|
||||||
|
|
||||||
|
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)
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
For example, cherrypicking objects from multiple derived classes
|
||||||
|
anywhere in the inheritance tree, using Q objects (with the
|
||||||
|
syntax: ``exact model name + three _ + field name``):
|
||||||
|
|
||||||
|
>>> ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
|
||||||
|
.
|
||||||
|
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
|
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
|
|
||||||
|
Combining Querysets
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Querysets could now be regarded as object containers that allow the
|
||||||
|
aggregation of different object types, very similar to python
|
||||||
|
lists - as long as the objects are accessed through the manager of
|
||||||
|
a common base class:
|
||||||
|
|
||||||
|
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
|
||||||
|
.
|
||||||
|
[ <ModelX: id 1, field_x (CharField)>,
|
||||||
|
<ModelY: id 2, field_y (CharField)> ]
|
||||||
|
|
||||||
|
|
||||||
|
ManyToManyField, ForeignKey, OneToOneField
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
E.g., if in your model you define::
|
||||||
|
|
||||||
|
field1 = OneToOneField(ModelA)
|
||||||
|
|
||||||
|
then field1 may now also refer to objects of type ``ModelB`` or ``ModelC``.
|
||||||
|
|
||||||
|
A ManyToManyField example::
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
>>> o=RelatingModel.objects.create()
|
||||||
|
>>> o.many2many.add(ModelA.objects.get(id=1))
|
||||||
|
>>> o.many2many.add(ModelB.objects.get(id=2))
|
||||||
|
>>> o.many2many.add(ModelC.objects.get(id=3))
|
||||||
|
|
||||||
|
>>> o.many2many.all()
|
||||||
|
[ <ModelA: id 1, field1 (CharField)>,
|
||||||
|
<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 MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
|
||||||
|
pass # or add fields
|
||||||
|
|
||||||
|
Or instead integrating the third party model anywhere into an
|
||||||
|
existing polymorphic inheritance tree::
|
||||||
|
|
||||||
|
class MyBaseModel(SomePolymorphicModel):
|
||||||
|
my_field = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
|
||||||
|
pass # or add fields
|
||||||
|
|
||||||
|
|
||||||
|
Non-Polymorphic Queries
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
If you insert ``.non_polymorphic()`` 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
|
||||||
|
(``ModelA`` in this example).
|
||||||
|
|
||||||
|
>>> qs=ModelA.objects.non_polymorphic().all()
|
||||||
|
>>> qs
|
||||||
|
[ <ModelA: id 1, field1 (CharField)>,
|
||||||
|
<ModelA: id 2, field1 (CharField)>,
|
||||||
|
<ModelA: id 3, field1 (CharField)> ]
|
||||||
|
|
||||||
|
There are no other changes in the behaviour of the queryset. For example,
|
||||||
|
enhancements for ``filter()`` or ``instance_of()`` etc. still work as expected.
|
||||||
|
If you do the final step yourself, you get the usual polymorphic result:
|
||||||
|
|
||||||
|
>>> ModelA.objects.get_real_instances(qs)
|
||||||
|
[ <ModelA: id 1, field1 (CharField)>,
|
||||||
|
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
|
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
|
|
||||||
|
About Queryset Methods
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* ``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()`` similarly supports the ``ModelX___field`` syntax
|
||||||
|
for specifying ordering through a field in a submodel.
|
||||||
|
|
||||||
|
* ``distinct()`` works as expected. It only regards the fields of
|
||||||
|
the base class, but this should never make a difference.
|
||||||
|
|
||||||
|
* ``select_related()`` works just as usual, but it can not (yet) be used
|
||||||
|
to select relations in derived models
|
||||||
|
(like ``ModelA.objects.select_related('ModelC___fieldxy')`` )
|
||||||
|
|
||||||
|
* ``extra()`` works as expected (it returns polymorphic results) but
|
||||||
|
currently has one restriction: The resulting objects are required to have
|
||||||
|
a unique primary key within the result set - otherwise an error is thrown
|
||||||
|
(this case could be made to work, however it may be mostly unneeded)..
|
||||||
|
The keyword-argument "polymorphic" is no longer supported.
|
||||||
|
You can get back the old non-polymorphic behaviour
|
||||||
|
by using ``ModelA.objects.non_polymorphic().extra(...)``.
|
||||||
|
|
||||||
|
* ``get_real_instances()`` allows you to turn a
|
||||||
|
queryset or list of base model objects efficiently into the real objects.
|
||||||
|
For example, you could do ``base_objects_queryset=ModelA.extra(...).non_polymorphic()``
|
||||||
|
and then call ``real_objects=base_objects_queryset.get_real_instances()``. Or alternatively
|
||||||
|
.``real_objects=ModelA.objects.get_real_instances(base_objects_queryset_or_object_list)``
|
||||||
|
|
||||||
|
* ``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.
|
||||||
|
|
||||||
|
* ``defer()`` and ``only()`` are not yet supported (support will be added
|
||||||
|
in the future).
|
||||||
|
|
||||||
|
|
||||||
|
Using enhanced Q-objects in any Places
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
The queryset enhancements (e.g. ``instance_of``) only work as arguments
|
||||||
|
to the member functions of a polymorphic queryset. Occationally it may
|
||||||
|
be useful to be able to use Q objects with these enhancements in other places.
|
||||||
|
As Django doesn't understand these enhanced Q objects, you need to
|
||||||
|
transform them manually into normal Q objects before you can feed them
|
||||||
|
to a Django queryset or function::
|
||||||
|
|
||||||
|
normal_q_object = ModelA.translate_polymorphic_Q_object( Q(instance_of=Model2B) )
|
||||||
|
|
||||||
|
This function cannot be used at model creation time however (in models.py),
|
||||||
|
as it may need to access the ContentTypes database table.
|
||||||
|
|
||||||
|
|
||||||
|
Nicely Displaying Polymorphic Querysets
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
In order to get the output as seen in all examples here, you need to use the
|
||||||
|
:class:`~polymorphic.showfields.ShowFieldType` class mixin::
|
||||||
|
|
||||||
|
from polymorphic import PolymorphicModel, ShowFieldType
|
||||||
|
|
||||||
|
class ModelA(ShowFieldType, PolymorphicModel):
|
||||||
|
field1 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
You may also use :class:`~polymorphic.showfields.ShowFieldContent`
|
||||||
|
or :class:`~polymorphic.showfields.ShowFieldTypeAndContent` to display
|
||||||
|
additional information when printing querysets (or converting them to text).
|
||||||
|
|
||||||
|
When showing field contents, they will be truncated to 20 characters. You can
|
||||||
|
modify this behaviour by setting a class variable in your model like this::
|
||||||
|
|
||||||
|
class ModelA(ShowFieldType, PolymorphicModel):
|
||||||
|
polymorphic_showfield_max_field_width = 20
|
||||||
|
...
|
||||||
|
|
||||||
|
Similarly, pre-V1.0 output formatting can be re-estated by using
|
||||||
|
``polymorphic_showfield_old_format = True``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. _restrictions:
|
||||||
|
|
||||||
|
Restrictions & Caveats
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* Database Performance regarding concrete Model inheritance in general.
|
||||||
|
Please see the :ref:`performance`.
|
||||||
|
|
||||||
|
* Queryset methods ``values()``, ``values_list()``, ``select_related()``,
|
||||||
|
``defer()`` and ``only()`` are not yet fully supported (see above).
|
||||||
|
``extra()`` has one restriction: the resulting objects are required to have
|
||||||
|
a unique primary key within the result set.
|
||||||
|
|
||||||
|
* 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``.
|
||||||
|
|
||||||
|
* When using the ``dumpdata`` management command on polymorphic tables
|
||||||
|
(or any table that has a reference to
|
||||||
|
:class:`~django.contrib.contenttypes.models.ContentType`),
|
||||||
|
include the ``--natural`` flag in the arguments.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. old links:
|
||||||
|
- http://code.djangoproject.com/wiki/ModelInheritance
|
||||||
|
- http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html
|
||||||
|
- http://www.djangosnippets.org/snippets/1031/
|
||||||
|
- http://www.djangosnippets.org/snippets/1034/
|
||||||
|
- http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&q=model+inheritance+CORBA#a20fabc661b7035d
|
||||||
|
- http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&q=inheritance#0b92971ffc0aa6f8
|
||||||
|
- http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&q=inheritance#d8c0af3dacad412d
|
||||||
|
- http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f
|
||||||
|
- http://peterbraden.co.uk/article/django-inheritance
|
||||||
|
- http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django
|
||||||
|
- 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/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses
|
||||||
|
- http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&q=polymorphic#e676a537d735d9ef
|
||||||
|
- http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&q=model+inheritance#bc18c18b2e83881e
|
||||||
|
- http://code.djangoproject.com/ticket/10808
|
||||||
|
- http://code.djangoproject.com/ticket/7270
|
||||||
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
Changelog
|
||||||
|
==========
|
||||||
|
|
||||||
|
Version 0.4.2 (2013-04-10)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* Used proper ``__version__`` marker.
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.4.1 (2013-04-10)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* Add Django 1.5 and 1.6 support
|
||||||
|
* Add proxy model support
|
||||||
|
* Add default admin ``list_filter`` for polymorphic model type.
|
||||||
|
* Fix queryset support of related objects.
|
||||||
|
* Performed an overall cleanup of the project
|
||||||
|
* **Deprecated** the ``queryset_class`` argument of the ``PolymorphicManager`` constructor, use the class attribute instead.
|
||||||
|
* **Dropped** Django 1.1, 1.2 and 1.3 support
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.4 (2013-03-25)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
* Update example project for Django 1.4
|
||||||
|
* Added tox and Travis configuration
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.3.1 (2013-02-28)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* SQL optimization, avoid query in pre_save_polymorphic()
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.3 (2013-02-28)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Many changes to the codebase happened, but no new version was released to pypi for years.
|
||||||
|
0.3 contains fixes submitted by many contributors, huge thanks to everyone!
|
||||||
|
|
||||||
|
* Added a polymorphic admin interface.
|
||||||
|
* PEP8 and code cleanups by various authors
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.2 (2012-11-12)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The 0.2 release serves as legacy release.
|
||||||
|
It supports Django 1.1 up till 1.4 and Python 2.4 up till 2.7.
|
||||||
|
|
||||||
|
For a detailed list of it's changes, see the :doc:`archived changelog <changelog_archive>`.
|
||||||
|
|
@ -1,56 +1,27 @@
|
||||||
*django_polymorphic*
|
Archive of old changelog entries
|
||||||
++++++++++++++++++++
|
================================
|
||||||
Changelog
|
|
||||||
++++++++++++++++++++
|
|
||||||
|
|
||||||
2013-02-28 Releasing Polymorphic v.0.3
|
|
||||||
======================================
|
|
||||||
|
|
||||||
Many changes to the codebase happened, but no new version was released to pypi
|
|
||||||
for years.
|
|
||||||
0.3 contains fixes submitted by many contributors, huge thanks to everyone!
|
|
||||||
|
|
||||||
|
|
||||||
Juli 5, 2012, Polymorphic admin interface
|
|
||||||
=========================================
|
|
||||||
|
|
||||||
Added a polymorphic admin interface. The admin interface is able to add polymorphic models,
|
|
||||||
and the admin edit screen also displays the custom fields of the polymorphic model.
|
|
||||||
|
|
||||||
|
|
||||||
2011-12-20 Renaming, refactoring, new maintainer
|
|
||||||
================================================
|
|
||||||
|
|
||||||
Since the original author diseappeared from the internet, we undertook to
|
|
||||||
maintain and upgrade this piece of software.
|
|
||||||
|
|
||||||
It works really well, but we cannot guarantee or otherwise support its current
|
|
||||||
state.
|
|
||||||
|
|
||||||
The latest "legacy" tag should be V1.0-RC-1. Anything above that should be
|
|
||||||
considered experimental and unstable until further notice (there be dragons).
|
|
||||||
|
|
||||||
New features, bug fixes and other improvements will be added to trunk from now
|
|
||||||
on.
|
|
||||||
|
|
||||||
|
|
||||||
2011-01-24 V1.0 Release Candidate 1
|
2011-01-24 V1.0 Release Candidate 1
|
||||||
===================================
|
------------------------------------
|
||||||
|
|
||||||
Bugfixes
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
* Fixed GitHub issue 15 (query result incomplete with inheritance).
|
* Fixed GitHub issue 15 (query result incomplete with inheritance).
|
||||||
Thanks to John Debs for reporting and the test case.
|
Thanks to John Debs for reporting and the test case.
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
2011-12-20 Renaming, refactoring, new maintainer
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Since the original author disappeared from the internet, we undertook to
|
||||||
|
maintain and upgrade this piece of software.
|
||||||
|
|
||||||
|
The latest "legacy" tag should be V1.0-RC-1. Anything above that should be
|
||||||
|
considered experimental and unstable until further notice (there be dragons).
|
||||||
|
|
||||||
|
New features, bug fixes and other improvements will be added to trunk from now on.
|
||||||
|
|
||||||
|
|
||||||
2010-11-11 V1.0 Beta 2
|
2010-11-11 V1.0 Beta 2
|
||||||
======================
|
-----------------------
|
||||||
|
|
||||||
This is a V1.0 Testing Release
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Beta 2 accumulated somewhat more changes than intended, and also
|
Beta 2 accumulated somewhat more changes than intended, and also
|
||||||
has been delayed by DBMS benchmark testing I wanted to do on model
|
has been delayed by DBMS benchmark testing I wanted to do on model
|
||||||
|
|
@ -58,8 +29,7 @@ inheritance. These benchmarks show that there are considerable
|
||||||
problems with concrete model inheritance and contemporary DBM systems.
|
problems with concrete model inheritance and contemporary DBM systems.
|
||||||
The results will be forthcoming on the google discussion forum.
|
The results will be forthcoming on the google discussion forum.
|
||||||
|
|
||||||
Please also see:
|
Please also see: http://www.jacobian.org/writing/concrete-inheritance/
|
||||||
http://www.jacobian.org/writing/concrete-inheritance/
|
|
||||||
|
|
||||||
The API should be stable now with Beta 2, so it's just about potential
|
The API should be stable now with Beta 2, so it's just about potential
|
||||||
bugfixes from now on regarding V1.0.
|
bugfixes from now on regarding V1.0.
|
||||||
|
|
@ -70,9 +40,8 @@ and Beta 1 is used on a few production sites by some enterprising users.
|
||||||
|
|
||||||
There will be a release candidate for V1.0 in the very near future.
|
There will be a release candidate for V1.0 in the very near future.
|
||||||
|
|
||||||
|
New Features and changes
|
||||||
New Features and API changes in Beta 2 since Beta 1
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
* API CHANGE: ``.extra()`` has been re-implemented. Now it's polymorphic by
|
* API CHANGE: ``.extra()`` has been re-implemented. Now it's polymorphic by
|
||||||
default and works (nearly) without restrictions (please see docs). This is a (very)
|
default and works (nearly) without restrictions (please see docs). This is a (very)
|
||||||
|
|
@ -109,7 +78,7 @@ New Features and API changes in Beta 2 since Beta 1
|
||||||
* misc changes/improvements
|
* misc changes/improvements
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
------------------------
|
~~~~~~~~
|
||||||
|
|
||||||
* Custom fields could cause problems when used as the primary key.
|
* Custom fields could cause problems when used as the primary key.
|
||||||
In derived models, Django's automatic ".pk" field does not always work
|
In derived models, Django's automatic ".pk" field does not always work
|
||||||
|
|
@ -121,14 +90,8 @@ Bugfixes
|
||||||
"python manage.py test polymorphic" also tests and reports on this problem now.
|
"python manage.py test polymorphic" also tests and reports on this problem now.
|
||||||
Thanks to Mathieu Steele for reporting and the test case.
|
Thanks to Mathieu Steele for reporting and the test case.
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-10-18 V1.0 Beta 1
|
2010-10-18 V1.0 Beta 1
|
||||||
======================
|
----------------------
|
||||||
|
|
||||||
This is a V1.0 Beta/Testing Release
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
This release is mostly a cleanup and maintenance release that also
|
This release is mostly a cleanup and maintenance release that also
|
||||||
improves a number of minor things and fixes one (non-critical) bug.
|
improves a number of minor things and fixes one (non-critical) bug.
|
||||||
|
|
@ -153,7 +116,7 @@ There also have been a number of minor API changes.
|
||||||
Please see the README for more information.
|
Please see the README for more information.
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
------------------------
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
* official Django 1.3 alpha compatibility
|
* official Django 1.3 alpha compatibility
|
||||||
|
|
||||||
|
|
@ -180,13 +143,13 @@ New Features
|
||||||
* Changelog added: CHANGES.rst/html
|
* Changelog added: CHANGES.rst/html
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
------------------------
|
~~~~~~~~
|
||||||
|
|
||||||
* Removed requirement for primary key to be an IntegerField.
|
* Removed requirement for primary key to be an IntegerField.
|
||||||
Thanks to Mathieu Steele and Malthe Borch.
|
Thanks to Mathieu Steele and Malthe Borch.
|
||||||
|
|
||||||
API Changes
|
API Changes
|
||||||
-----------
|
~~~~~~~~~~~
|
||||||
|
|
||||||
**polymorphic_dumpdata**
|
**polymorphic_dumpdata**
|
||||||
|
|
||||||
|
|
@ -223,10 +186,8 @@ Django 1.3 requires ``python manage.py test polymorphic`` instead of
|
||||||
just ``python manage.py test``.
|
just ``python manage.py test``.
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-2-22
|
2010-2-22
|
||||||
==========
|
---------
|
||||||
|
|
||||||
IMPORTANT: API Changed (import path changed), and Installation Note
|
IMPORTANT: API Changed (import path changed), and Installation Note
|
||||||
|
|
||||||
|
|
@ -253,12 +214,12 @@ imported directly from 'polymorphic' instead from
|
||||||
+ minor API addition: 'from polymorphic import VERSION, get_version'
|
+ minor API addition: 'from polymorphic import VERSION, get_version'
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
------------------------
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
Python 2.4 compatibility, contributed by Charles Leifer. Thanks!
|
Python 2.4 compatibility, contributed by Charles Leifer. Thanks!
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
------------------------
|
~~~~~~~~
|
||||||
|
|
||||||
Fix: The exception "...has no attribute 'sub_and_superclass_dict'"
|
Fix: The exception "...has no attribute 'sub_and_superclass_dict'"
|
||||||
could be raised. (This occurred if a subclass defined __init__
|
could be raised. (This occurred if a subclass defined __init__
|
||||||
|
|
@ -272,14 +233,11 @@ Now it is possible to give a field the same name as the class
|
||||||
(Found through the example provided by Mattias Brändström)
|
(Found through the example provided by Mattias Brändström)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-2-4
|
2010-2-4
|
||||||
==========
|
--------
|
||||||
|
|
||||||
New features (and documentation)
|
New features (and documentation)
|
||||||
-----------------------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
queryset order_by method added
|
queryset order_by method added
|
||||||
|
|
||||||
|
|
@ -296,7 +254,7 @@ More about these additions in the docs:
|
||||||
http://bserve.webhop.org/wiki/django_polymorphic/doc
|
http://bserve.webhop.org/wiki/django_polymorphic/doc
|
||||||
|
|
||||||
Bugfixes
|
Bugfixes
|
||||||
------------------------
|
~~~~~~~~
|
||||||
|
|
||||||
* fix remaining potential accessor name clashes (but this only works
|
* fix remaining potential accessor name clashes (but this only works
|
||||||
with Django 1.2+, for 1.1 no changes). Thanks to Andrew Ingram.
|
with Django 1.2+, for 1.1 no changes). Thanks to Andrew Ingram.
|
||||||
|
|
@ -307,7 +265,7 @@ Bugfixes
|
||||||
sel.-r. was just ignored)
|
sel.-r. was just ignored)
|
||||||
|
|
||||||
"Restrictions & Caveats" updated
|
"Restrictions & Caveats" updated
|
||||||
----------------------------------------
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* Django 1.1 only - the names of polymorphic models must be unique
|
* Django 1.1 only - the names of polymorphic models must be unique
|
||||||
in the whole project, even if they are in two different apps.
|
in the whole project, even if they are in two different apps.
|
||||||
|
|
@ -320,11 +278,8 @@ Bugfixes
|
||||||
support for natural keys in serialization).
|
support for natural keys in serialization).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-1-30
|
2010-1-30
|
||||||
==========
|
---------
|
||||||
|
|
||||||
Fixed ContentType related field accessor clash (an error emitted
|
Fixed ContentType related field accessor clash (an error emitted
|
||||||
by model validation) by adding related_name to the ContentType
|
by model validation) by adding related_name to the ContentType
|
||||||
|
|
@ -332,11 +287,8 @@ ForeignKey. This happened if your polymorphc model used a ContentType
|
||||||
ForeignKey. Thanks to Andrew Ingram.
|
ForeignKey. Thanks to Andrew Ingram.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-1-29
|
2010-1-29
|
||||||
==========
|
---------
|
||||||
|
|
||||||
Restructured django_polymorphic into a regular Django add-on
|
Restructured django_polymorphic into a regular Django add-on
|
||||||
application. This is needed for the management commands, and
|
application. This is needed for the management commands, and
|
||||||
|
|
@ -348,11 +300,8 @@ The ``poly`` app - until now being used for test purposes only
|
||||||
("installation/testing") for more info.
|
("installation/testing") for more info.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-1-28
|
2010-1-28
|
||||||
==========
|
---------
|
||||||
|
|
||||||
Added the polymorphic_dumpdata management command (github issue 4),
|
Added the polymorphic_dumpdata management command (github issue 4),
|
||||||
for creating fixtures, this should be used instead of
|
for creating fixtures, this should be used instead of
|
||||||
|
|
@ -363,12 +312,8 @@ Important: Using ContentType together with dumpdata generally
|
||||||
needs Django 1.2 (important as any polymorphic model uses
|
needs Django 1.2 (important as any polymorphic model uses
|
||||||
ContentType).
|
ContentType).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------
|
|
||||||
|
|
||||||
2010-1-26
|
2010-1-26
|
||||||
==========
|
---------
|
||||||
|
|
||||||
IMPORTANT - database schema change (more info in change log).
|
IMPORTANT - database schema change (more info in change log).
|
||||||
I hope I got this change in early enough before anyone started
|
I hope I got this change in early enough before anyone started
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# django-polymorphic documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Sun May 19 12:20:47 2013.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
sys.path.insert(0, os.path.abspath('_ext'))
|
||||||
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings'
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.graphviz',
|
||||||
|
'sphinx.ext.intersphinx'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'django-polymorphic'
|
||||||
|
copyright = u'2013, Bert Constantin, Chris Glass, Diederik van der Boor'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.4.2.dev0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.4.2'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
html_theme = 'default'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
#html_theme_path = []
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_domain_indices = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_sphinx = True
|
||||||
|
|
||||||
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
|
#html_show_copyright = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = None
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'django-polymorphicdoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'django-polymorphic.tex', u'django-polymorphic Documentation',
|
||||||
|
u'Bert Constantin, Chris Glass, Diederik van der Boor', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# If true, show page references after internal links.
|
||||||
|
#latex_show_pagerefs = False
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#latex_show_urls = False
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
('index', 'django-polymorphic', u'django-polymorphic Documentation',
|
||||||
|
[u'Bert Constantin, Chris Glass, Diederik van der Boor'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
# If true, show URL addresses after external links.
|
||||||
|
#man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'django-polymorphic', u'django-polymorphic Documentation',
|
||||||
|
u'Bert Constantin, Chris Glass, Diederik van der Boor', 'django-polymorphic', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#texinfo_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#texinfo_domain_indices = True
|
||||||
|
|
||||||
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
|
#texinfo_show_urls = 'footnote'
|
||||||
|
|
||||||
|
|
||||||
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
|
intersphinx_mapping = {
|
||||||
|
#'http://docs.python.org/': None,
|
||||||
|
'https://docs.djangoproject.com/en/dev': 'https://docs.djangoproject.com/en/dev/_objects',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
Contributing
|
||||||
|
============
|
||||||
|
|
||||||
|
You can contribute to *django-polymorphic* to forking the code on GitHub:
|
||||||
|
|
||||||
|
https://github.com/chrisglass/django_polymorphic
|
||||||
|
|
||||||
|
|
||||||
|
Running tests
|
||||||
|
-------------
|
||||||
|
|
||||||
|
We require features to be backed by a unit test.
|
||||||
|
This way, we can test *django-polymorphic* against new Django versions.
|
||||||
|
To run the included test suite, execute::
|
||||||
|
|
||||||
|
./runtests.py
|
||||||
|
|
||||||
|
To test support for multiple Python and Django versions, run tox from the repository root::
|
||||||
|
|
||||||
|
pip install tox
|
||||||
|
tox
|
||||||
|
|
||||||
|
The Python versions need to be installed at your system.
|
||||||
|
On Linux, download the versions at http://www.python.org/download/releases/.
|
||||||
|
On MacOS X, use Homebrew_ to install other Python versions.
|
||||||
|
|
||||||
|
We currently support Python 2.6 and 2.7.
|
||||||
|
Python 3.3 support is being worked on.
|
||||||
|
|
||||||
|
|
||||||
|
Example project
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The repository (or tar file) contains a complete Django project
|
||||||
|
that may be used for tests or experiments, without any installation needed.
|
||||||
|
|
||||||
|
The management command ``pcmd.py`` in the app ``pexp`` can be used for quick tests
|
||||||
|
or experiments - modify this file (pexp/management/commands/pcmd.py) to your liking.
|
||||||
|
|
||||||
|
|
||||||
|
Supported Django versions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The current release should be usable with the supported releases of Django;
|
||||||
|
the current stable release and the previous release. Supporting older Django
|
||||||
|
versions is a nice-to-have feature, but not mandatory.
|
||||||
|
|
||||||
|
In case you need to use *django-polymorphic* with older Django versions,
|
||||||
|
consider installing a previous version.
|
||||||
|
|
||||||
|
.. _Homebrew: http://mxcl.github.io/homebrew/
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
Welcome to django-polymorphic's documentation!
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
Django-polymorphic simplifies using inherited models in Django projects.
|
||||||
|
When a query is made at the base model, the inherited model classes are returned.
|
||||||
|
|
||||||
|
When we store models that derive from a ``Project`` model...
|
||||||
|
|
||||||
|
>>> Project.objects.create(topic="Department Party")
|
||||||
|
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
|
||||||
|
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
||||||
|
|
||||||
|
...and want to retrieve all our projects, the subclassed models are returned!
|
||||||
|
|
||||||
|
>>> Project.objects.all()
|
||||||
|
[ <Project: id 1, topic "Department Party">,
|
||||||
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
|
|
||||||
|
Using vanilla Django, we get the base class objects, which is rarely what we wanted:
|
||||||
|
|
||||||
|
>>> Project.objects.all()
|
||||||
|
[ <Project: id 1, topic "Department Party">,
|
||||||
|
<Project: id 2, topic "Painting with Tim">,
|
||||||
|
<Project: id 3, topic "Swallow Aerodynamics"> ]
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Full admin integation.
|
||||||
|
* ORM integration:
|
||||||
|
|
||||||
|
* support for ForeignKey, ManyToManyField, OneToOneField descriptors.
|
||||||
|
* support for proxy models
|
||||||
|
* Filtering/ordering of derived models (``ArtProject___artist``).
|
||||||
|
* Filtering model types: ``instance_of(...)`` and ``not_instance_of(...)``
|
||||||
|
* Combining querysets of different models (``qs3 = qs1 | qs2``)
|
||||||
|
* Support for custom user-defined managers.
|
||||||
|
|
||||||
|
* Uses the minumum amount of queries needed to fetch the derived models.
|
||||||
|
* Disabling polymorphic behavior when needed.
|
||||||
|
|
||||||
|
|
||||||
|
Getting started
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
quickstart
|
||||||
|
admin
|
||||||
|
performance
|
||||||
|
|
||||||
|
Advanced topics
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
advanced
|
||||||
|
managers
|
||||||
|
changelog
|
||||||
|
contributing
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
|
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set BUILDDIR=_build
|
||||||
|
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||||
|
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||||
|
if NOT "%PAPER%" == "" (
|
||||||
|
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||||
|
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
if "%1" == "help" (
|
||||||
|
:help
|
||||||
|
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||||
|
echo. html to make standalone HTML files
|
||||||
|
echo. dirhtml to make HTML files named index.html in directories
|
||||||
|
echo. singlehtml to make a single large HTML file
|
||||||
|
echo. pickle to make pickle files
|
||||||
|
echo. json to make JSON files
|
||||||
|
echo. htmlhelp to make HTML files and a HTML help project
|
||||||
|
echo. qthelp to make HTML files and a qthelp project
|
||||||
|
echo. devhelp to make HTML files and a Devhelp project
|
||||||
|
echo. epub to make an epub
|
||||||
|
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||||
|
echo. text to make text files
|
||||||
|
echo. man to make manual pages
|
||||||
|
echo. texinfo to make Texinfo files
|
||||||
|
echo. gettext to make PO message catalogs
|
||||||
|
echo. changes to make an overview over all changed/added/deprecated items
|
||||||
|
echo. linkcheck to check all external links for integrity
|
||||||
|
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "clean" (
|
||||||
|
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||||
|
del /q /s %BUILDDIR%\*
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "html" (
|
||||||
|
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "dirhtml" (
|
||||||
|
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "singlehtml" (
|
||||||
|
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "pickle" (
|
||||||
|
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can process the pickle files.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "json" (
|
||||||
|
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can process the JSON files.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "htmlhelp" (
|
||||||
|
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||||
|
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "qthelp" (
|
||||||
|
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||||
|
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||||
|
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-polymorphic.qhcp
|
||||||
|
echo.To view the help file:
|
||||||
|
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-polymorphic.ghc
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "devhelp" (
|
||||||
|
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "epub" (
|
||||||
|
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "latex" (
|
||||||
|
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "text" (
|
||||||
|
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "man" (
|
||||||
|
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "texinfo" (
|
||||||
|
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "gettext" (
|
||||||
|
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "changes" (
|
||||||
|
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.The overview file is in %BUILDDIR%/changes.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "linkcheck" (
|
||||||
|
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Link check complete; look for any errors in the above output ^
|
||||||
|
or in %BUILDDIR%/linkcheck/output.txt.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "doctest" (
|
||||||
|
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Testing of doctests in the sources finished, look at the ^
|
||||||
|
results in %BUILDDIR%/doctest/output.txt.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
:end
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
|
||||||
|
|
||||||
|
Custom Managers, Querysets & Manager Inheritance
|
||||||
|
================================================
|
||||||
|
|
||||||
|
Using a Custom Manager
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
A nice feature of Django is the possibility to define one's own custom object managers.
|
||||||
|
This is fully supported with django_polymorphic: For creating a custom polymorphic
|
||||||
|
manager class, just derive your manager from ``PolymorphicManager`` instead of
|
||||||
|
``models.Manager``. 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
|
||||||
|
|
||||||
|
class TimeOrderedManager(PolymorphicManager):
|
||||||
|
def get_query_set(self):
|
||||||
|
qs = super(TimeOrderedManager,self).get_query_set()
|
||||||
|
return qs.order_by('-start_date') # order the queryset
|
||||||
|
|
||||||
|
def most_recent(self):
|
||||||
|
qs = self.get_query_set() # get my ordered queryset
|
||||||
|
return qs[:10] # limit => get ten most recent entries
|
||||||
|
|
||||||
|
class Project(PolymorphicModel):
|
||||||
|
objects = PolymorphicManager() # add the default polymorphic manager first
|
||||||
|
objects_ordered = TimeOrderedManager() # then add your own manager
|
||||||
|
start_date = DateTimeField() # project start is this date/time
|
||||||
|
|
||||||
|
The first manager defined ('objects' in the example) is used by
|
||||||
|
Django as automatic manager for several purposes, including accessing
|
||||||
|
related objects. It must not filter objects and it's safest to use
|
||||||
|
the plain ``PolymorphicManager`` here.
|
||||||
|
|
||||||
|
Manager Inheritance
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Polymorphic models inherit/propagate all managers from their
|
||||||
|
base models, as long as these are polymorphic. This means that all
|
||||||
|
managers defined in polymorphic base models continue to work as
|
||||||
|
expected in models inheriting from this base model::
|
||||||
|
|
||||||
|
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||||
|
|
||||||
|
class TimeOrderedManager(PolymorphicManager):
|
||||||
|
def get_query_set(self):
|
||||||
|
qs = super(TimeOrderedManager,self).get_query_set()
|
||||||
|
return qs.order_by('-start_date') # order the queryset
|
||||||
|
|
||||||
|
def most_recent(self):
|
||||||
|
qs = self.get_query_set() # get my ordered queryset
|
||||||
|
return qs[:10] # limit => get ten most recent entries
|
||||||
|
|
||||||
|
class Project(PolymorphicModel):
|
||||||
|
objects = PolymorphicManager() # add the default polymorphic manager first
|
||||||
|
objects_ordered = TimeOrderedManager() # then add your own manager
|
||||||
|
start_date = DateTimeField() # project start is this date/time
|
||||||
|
|
||||||
|
class ArtProject(Project): # inherit from Project, inheriting its fields and managers
|
||||||
|
artist = models.CharField(max_length=30)
|
||||||
|
|
||||||
|
ArtProject inherited the managers ``objects`` and ``objects_ordered`` from Project.
|
||||||
|
|
||||||
|
``ArtProject.objects_ordered.all()`` will return all art projects ordered
|
||||||
|
regarding their start time and ``ArtProject.objects_ordered.most_recent()``
|
||||||
|
will return the ten most recent art projects.
|
||||||
|
.
|
||||||
|
|
||||||
|
Using a Custom Queryset Class
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The ``PolymorphicManager`` 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::
|
||||||
|
|
||||||
|
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
||||||
|
|
||||||
|
class MyQuerySet(PolymorphicQuerySet):
|
||||||
|
def my_queryset_method(...):
|
||||||
|
...
|
||||||
|
|
||||||
|
class MyModel(PolymorphicModel):
|
||||||
|
my_objects=PolymorphicManager(MyQuerySet)
|
||||||
|
...
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
.. _performance:
|
||||||
|
|
||||||
|
Performance Considerations
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Usually, when Django users create their own polymorphic ad-hoc solution
|
||||||
|
without a tool like *django-polymorphic*, this usually results in a variation of ::
|
||||||
|
|
||||||
|
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
||||||
|
|
||||||
|
which has very bad performance, as it introduces one additional
|
||||||
|
SQL query for every object in the result which is not of class ``BaseModel``.
|
||||||
|
Compared to these solutions, *django-polymorphic* has the advantage
|
||||||
|
that it only needs 1 SQL query per *object type*, and not *per object*.
|
||||||
|
|
||||||
|
The current implementation is does not use any custom SQL or Django DB layer
|
||||||
|
internals - it is purely based on the standard Django ORM. Specifically, 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 SQL query if all are
|
||||||
|
class ``ModelA``. If 50 objects are ``ModelA`` and 50 are ``ModelB``, 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 ``ModelA``).
|
||||||
|
|
||||||
|
Database notes
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Current relational DBM systems seem to have general problems with
|
||||||
|
the SQL queries produced by object relational mappers like the Django
|
||||||
|
ORM, if these use multi-table inheritance like Django's ORM does.
|
||||||
|
The "inner joins" in these queries can perform very badly.
|
||||||
|
This is independent of django_polymorphic and affects all uses of
|
||||||
|
multi table Model inheritance.
|
||||||
|
|
||||||
|
Please also see this `post (and comments) from Jacob Kaplan-Moss
|
||||||
|
<http://www.jacobian.org/writing/concrete-inheritance/>`_.
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
Quickstart
|
||||||
|
===========
|
||||||
|
|
||||||
|
Install the project using::
|
||||||
|
|
||||||
|
pip install django-polymorphic
|
||||||
|
|
||||||
|
Update the settings file::
|
||||||
|
|
||||||
|
INSTALLED_APPS += (
|
||||||
|
'polymorphic',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
)
|
||||||
|
|
||||||
|
The current release of *django-polymorphic* supports Django 1.4, 1.5 and 1.6.
|
||||||
|
|
||||||
|
Making 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.
|
||||||
|
|
||||||
|
Using Polymorphic Models
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Create some objects:
|
||||||
|
|
||||||
|
>>> Project.objects.create(topic="Department Party")
|
||||||
|
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
|
||||||
|
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
||||||
|
|
||||||
|
Get polymorphic query results:
|
||||||
|
|
||||||
|
>>> Project.objects.all()
|
||||||
|
[ <Project: id 1, topic "Department Party">,
|
||||||
|
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
|
|
||||||
|
Use ``instance_of`` or ``not_instance_of`` for narrowing the result to specific subtypes:
|
||||||
|
|
||||||
|
>>> Project.objects.instance_of(ArtProject)
|
||||||
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner"> ]
|
||||||
|
|
||||||
|
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
|
||||||
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
|
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
|
||||||
|
|
||||||
|
Polymorphic filtering: Get all projects where Mr. Turner is involved as an artist
|
||||||
|
or supervisor (note the three underscores):
|
||||||
|
|
||||||
|
>>> Project.objects.filter(Q(ArtProject___artist='T. Turner') | Q(ResearchProject___supervisor='T. Turner'))
|
||||||
|
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
|
||||||
|
<ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
|
||||||
|
|
||||||
|
This is basically all you need to know, as *django-polymorphic* mostly
|
||||||
|
works fully automatic and just delivers the expected results.
|
||||||
|
|
||||||
|
Note: When using the ``dumpdata`` management command on polymorphic tables
|
||||||
|
(or any table that has a reference to :class:`~django.contrib.contenttypes.models.ContentType`),
|
||||||
|
include the ``--natural`` flag in the arguments. This makes sure the
|
||||||
|
:class:`~django.contrib.contenttypes.models.ContentType` models will be referenced by name
|
||||||
|
instead of their primary key as that changes between Django instances.
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
While *django-polymorphic* makes subclassed models easy to use in Django,
|
||||||
|
we still encourage to use them with caution. Each subclassed model will require
|
||||||
|
Django to perform an ``INNER JOIN`` to fetch the model fields from the database.
|
||||||
|
While taking this in mind, there are valid reasons for using subclassed models.
|
||||||
|
That's what this library is designed for!
|
||||||
|
|
@ -248,6 +248,7 @@ class PolymorphicTests(TestCase):
|
||||||
"""
|
"""
|
||||||
def test_diamond_inheritance(self):
|
def test_diamond_inheritance(self):
|
||||||
# Django diamond problem
|
# Django diamond problem
|
||||||
|
# https://code.djangoproject.com/ticket/10808
|
||||||
o1 = DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')
|
o1 = DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')
|
||||||
o2 = DiamondXY.objects.get()
|
o2 = DiamondXY.objects.get()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import sys,os
|
|
||||||
|
|
||||||
dopart = None
|
|
||||||
if len(sys.argv)>1: dopart = sys.argv[1]
|
|
||||||
noshow = 'noshow' in sys.argv
|
|
||||||
|
|
||||||
css='--stylesheet-path=rst.css'
|
|
||||||
|
|
||||||
def conv(name):
|
|
||||||
print 'convert',name
|
|
||||||
if noshow:
|
|
||||||
os.system('rst2html.py '+css+' %s.rst >%s.html' % (name, name) )
|
|
||||||
else:
|
|
||||||
os.system('rst2html.py '+css+' %s.rst >%s.html ; firefox %s.html' % (name, name, name) )
|
|
||||||
|
|
||||||
if not dopart or dopart=='1': conv('DOCS')
|
|
||||||
if not dopart or dopart=='2': conv('README')
|
|
||||||
if not dopart or dopart=='3': conv('CHANGES')
|
|
||||||
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
206
rst.css
206
rst.css
|
|
@ -1,206 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 }
|
|
||||||
Loading…
Reference in New Issue