updated docs
parent
c2b420aea0
commit
e51425f5df
|
|
@ -5,13 +5,18 @@
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
.settings
|
.settings
|
||||||
pprep.py
|
|
||||||
|
mypoly.py
|
||||||
|
|
||||||
|
tmp
|
||||||
|
libraries-local
|
||||||
|
|
||||||
pushgit
|
pushgit
|
||||||
pushhg
|
pushhg
|
||||||
pushreg
|
pushreg
|
||||||
mypoly.py
|
|
||||||
tmp
|
ppreadme.py
|
||||||
poly2.py
|
ppdocs.py
|
||||||
libraries-local
|
README.html
|
||||||
README.html
|
DOCS.html
|
||||||
|
|
||||||
|
|
|
||||||
124
DOCS.rst
124
DOCS.rst
|
|
@ -1,22 +1,5 @@
|
||||||
===============================
|
.. contents:: Table of Contents
|
||||||
Fully Polymorphic Django Models
|
:depth: 1
|
||||||
===============================
|
|
||||||
|
|
||||||
|
|
||||||
'polymorphic.py' is an add-on module that adds automatic
|
|
||||||
polymorphism to the Django model inheritance system.
|
|
||||||
|
|
||||||
The effect is: For enabled models, objects retrieved from the
|
|
||||||
database are always delivered just as they were created and saved,
|
|
||||||
with the same type/class and fields - regardless how they are
|
|
||||||
retrieved. The resulting querysets are polymorphic, i.e. may deliver
|
|
||||||
objects of several different types in a single query result.
|
|
||||||
|
|
||||||
Please see the examples below as they demonstrate this best.
|
|
||||||
|
|
||||||
Please note that this module is still very experimental. See below for
|
|
||||||
current restrictions, caveats, and performance implications.
|
|
||||||
|
|
||||||
|
|
||||||
Installation / Testing
|
Installation / Testing
|
||||||
======================
|
======================
|
||||||
|
|
@ -54,8 +37,8 @@ views.py files live).
|
||||||
Defining Polymorphic Models
|
Defining Polymorphic Models
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
To make models polymorphic, use PolymorphicModel instead of Django's
|
To make models polymorphic, use ``PolymorphicModel`` instead of Django's
|
||||||
models.Model as the superclass of your base model. All models
|
``models.Model`` as the superclass of your base model. All models
|
||||||
inheriting from your base class will be polymorphic as well::
|
inheriting from your base class will be polymorphic as well::
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel
|
from polymorphic import PolymorphicModel
|
||||||
|
|
@ -110,7 +93,7 @@ Polymorphic filtering (for fields in derived classes)
|
||||||
|
|
||||||
For example, cherrypicking objects from multiple derived classes
|
For example, cherrypicking objects from multiple derived classes
|
||||||
anywhere in the inheritance tree, using Q objects (with the
|
anywhere in the inheritance tree, using Q objects (with the
|
||||||
slightly enhanced syntax: exact model name + three _ + field name):
|
syntax: ``exact model name + three _ + field name``):
|
||||||
|
|
||||||
>>> ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
|
>>> ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
|
||||||
.
|
.
|
||||||
|
|
@ -163,7 +146,7 @@ ManyToManyField, ForeignKey, OneToOneField
|
||||||
|
|
||||||
field1 = OneToOneField(ModelA)
|
field1 = OneToOneField(ModelA)
|
||||||
|
|
||||||
then field1 may now also refer to objects of type ModelB or ModelC.
|
then field1 may now also refer to objects of type ``ModelB`` or ``ModelC``.
|
||||||
|
|
||||||
A ManyToManyField example::
|
A ManyToManyField example::
|
||||||
|
|
||||||
|
|
@ -202,7 +185,7 @@ Using a Custom Manager
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
For creating a custom polymorphic manager class, derive your manager
|
For creating a custom polymorphic manager class, derive your manager
|
||||||
from PolymorphicManager instead of models.Manager. In your model
|
from ``PolymorphicManager`` instead of ``models.Manager``. In your model
|
||||||
class, explicitly add the default manager first, and then your
|
class, explicitly add the default manager first, and then your
|
||||||
custom manager::
|
custom manager::
|
||||||
|
|
||||||
|
|
@ -217,7 +200,7 @@ custom manager::
|
||||||
The first manager defined ('objects' in the example) is used by
|
The first manager defined ('objects' in the example) is used by
|
||||||
Django as automatic manager for several purposes, including accessing
|
Django as automatic manager for several purposes, including accessing
|
||||||
related objects. It must not filter objects and it's safest to use
|
related objects. It must not filter objects and it's safest to use
|
||||||
the plain PolymorphicManager here.
|
the plain ``PolymorphicManager`` here.
|
||||||
|
|
||||||
Manager Inheritance / Propagation
|
Manager Inheritance / Propagation
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
@ -230,7 +213,7 @@ An example (inheriting from MyModel above)::
|
||||||
class MyModel2(MyModel):
|
class MyModel2(MyModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Managers inherited from MyModel, delivering MyModel2 objects (including MyModel2 subclass objects)
|
# Managers inherited from MyModel, delivering MyModel2 (and subclass) objects
|
||||||
>>> MyModel2.objects.all()
|
>>> MyModel2.objects.all()
|
||||||
>>> MyModel2.ordered_objects.all()
|
>>> MyModel2.ordered_objects.all()
|
||||||
|
|
||||||
|
|
@ -242,7 +225,7 @@ managers are always fully propagated from all polymorphic base models
|
||||||
Using a Custom Queryset Class
|
Using a Custom Queryset Class
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
The PolymorphicManager class accepts one initialization argument,
|
The ``PolymorphicManager`` class accepts one initialization argument,
|
||||||
which is the queryset class the manager should use. A custom
|
which is the queryset class the manager should use. A custom
|
||||||
custom queryset class can be defined and used like this::
|
custom queryset class can be defined and used like this::
|
||||||
|
|
||||||
|
|
@ -259,51 +242,51 @@ Performance Considerations
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
The current implementation is pretty simple and does not use any
|
The current implementation is pretty simple and does not use any
|
||||||
custom sql - it is purely based on the Django ORM. Right now the
|
custom SQL - it is purely based on the Django ORM. Right now the
|
||||||
query ::
|
query ::
|
||||||
|
|
||||||
result_objects = list( ModelA.objects.filter(...) )
|
result_objects = list( ModelA.objects.filter(...) )
|
||||||
|
|
||||||
performs one sql query to retrieve ModelA objects and one additional
|
performs one SQL query to retrieve ``ModelA`` objects and one additional
|
||||||
query for each unique derived class occurring in result_objects.
|
query for each unique derived class occurring in result_objects.
|
||||||
The best case for retrieving 100 objects is 1 db query if all are
|
The best case for retrieving 100 objects is 1 db query if all are
|
||||||
class ModelA. If 50 objects are ModelA and 50 are ModelB, then two
|
class ``ModelA``. If 50 objects are ``ModelA`` and 50 are ModelB, then two
|
||||||
queries are executed. If result_objects contains only the base model
|
queries are executed. If result_objects contains only the base model
|
||||||
type (ModelA), the polymorphic models are just as efficient as plain
|
type (``ModelA``), the polymorphic models are just as efficient as plain
|
||||||
Django models (in terms of executed queries). The pathological worst
|
Django models (in terms of executed queries). The pathological worst
|
||||||
case is 101 db queries if result_objects contains 100 different
|
case is 101 db queries if result_objects contains 100 different
|
||||||
object types (with all of them subclasses of ModelA).
|
object types (with all of them subclasses of ``ModelA``).
|
||||||
|
|
||||||
Performance ist relative: when Django users create their own
|
Performance ist relative: when Django users create their own
|
||||||
polymorphic ad-hoc solution (without a module like polymorphic.py),
|
polymorphic ad-hoc solution (without a module like ``polymorphic.py``),
|
||||||
this usually results in a variation of ::
|
this usually results in a variation of ::
|
||||||
|
|
||||||
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
|
||||||
|
|
||||||
which of has really bad performance. Relative to this, the
|
which of has really bad performance. Relative to this, the
|
||||||
performance of the current polymorphic.py is pretty good.
|
performance of the current ``polymorphic.py`` is pretty good.
|
||||||
It may well be efficient enough for the majority of use cases.
|
It may well be efficient enough for the majority of use cases.
|
||||||
|
|
||||||
Chunking: The implementation always requests objects in chunks of
|
Chunking: The implementation always requests objects in chunks of
|
||||||
size Polymorphic_QuerySet_objects_per_request. This limits the
|
size ``Polymorphic_QuerySet_objects_per_request``. This limits the
|
||||||
complexity/duration for each query, including the pathological cases.
|
complexity/duration for each query, including the pathological cases.
|
||||||
|
|
||||||
|
|
||||||
Possible Optimizations
|
Possible Optimizations
|
||||||
======================
|
======================
|
||||||
|
|
||||||
PolymorphicQuerySet can be optimized to require only one SQL query
|
``PolymorphicQuerySet`` can be optimized to require only one SQL query
|
||||||
for the queryset evaluation and retrieval of all objects.
|
for the queryset evaluation and retrieval of all objects.
|
||||||
|
|
||||||
Basically, what ist needed is a possibility to pull in the fields
|
Basically, what ist needed is a possibility to pull in the fields
|
||||||
from all relevant sub-models with one sql query. However, some deeper
|
from all relevant sub-models with one SQL query. However, some deeper
|
||||||
digging into the Django database layer will be required in order to
|
digging into the Django database layer will be required in order to
|
||||||
make this happen.
|
make this happen.
|
||||||
|
|
||||||
A viable option might be to get the sql query from the QuerySet
|
A viable option might be to get the SQL query from the QuerySet
|
||||||
(probably from django.db.models.sql.compiler.SQLCompiler.as_sql),
|
(probably from ``django.db.models.SQL.compiler.SQLCompiler.as_sql``),
|
||||||
making sure that all necessary joins are done, and then doing a
|
making sure that all necessary joins are done, and then doing a
|
||||||
custom SQL request from there (like in SQLCompiler.execute_sql).
|
custom SQL request from there (like in ``SQLCompiler.execute_sql``).
|
||||||
|
|
||||||
An optimized version could fall back to the current ORM-only
|
An optimized version could fall back to the current ORM-only
|
||||||
implementation for all non-SQL databases.
|
implementation for all non-SQL databases.
|
||||||
|
|
@ -311,9 +294,9 @@ implementation for all non-SQL databases.
|
||||||
SQL Complexity
|
SQL Complexity
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
With only one sql query, one sql join for each possible subclass
|
With only one SQL query, one SQL join for each possible subclass
|
||||||
would be needed (BaseModel.__subclasses__(), recursively).
|
would be needed (``BaseModel.__subclasses__()``, recursively).
|
||||||
With two sql queries, the number of joins could be reduced to the
|
With two SQL queries, the number of joins could be reduced to the
|
||||||
number of actuallly occurring subclasses in the result. A final
|
number of actuallly occurring subclasses in the result. A final
|
||||||
implementation might want to use one query only if the number of
|
implementation might want to use one query only if the number of
|
||||||
possible subclasses (and therefore joins) is not too large, and
|
possible subclasses (and therefore joins) is not too large, and
|
||||||
|
|
@ -328,47 +311,55 @@ will actually be involved in typical use cases - the total number
|
||||||
of classes in the inheritance tree as well as the number of distinct
|
of classes in the inheritance tree as well as the number of distinct
|
||||||
classes in query results. It may well turn out that the increased
|
classes in query results. It may well turn out that the increased
|
||||||
number of joins is no problem for the DBMS in all realistic use
|
number of joins is no problem for the DBMS in all realistic use
|
||||||
cases. Alternatively, if the sql query execution time is
|
cases. Alternatively, if the SQL query execution time is
|
||||||
significantly longer even in common use cases, this may still be
|
significantly longer even in common use cases, this may still be
|
||||||
acceptable in exchange for the added functionality.
|
acceptable in exchange for the added functionality.
|
||||||
|
|
||||||
Let's not forget that all of the above is just about optimizations.
|
General
|
||||||
The current simplistic implementation already works well - perhaps
|
-------------------
|
||||||
well enough for the majority of applications.
|
|
||||||
|
Let's not forget that all of the above is just about optimization.
|
||||||
|
The current implementation already works well - and perhaps well
|
||||||
|
enough for the majority of applications.
|
||||||
|
|
||||||
|
Also, it seems that further optimization (down to one DB request)
|
||||||
|
would be restricted to a small area of the code, straightforward
|
||||||
|
to implement, and mostly independent from the rest of the module.
|
||||||
|
So this optimization can be done at any later time (like when
|
||||||
|
it's needed).
|
||||||
|
|
||||||
|
|
||||||
Loose Ends
|
Unsupported Methods, Restrictions & Caveats
|
||||||
==========
|
===========================================
|
||||||
|
|
||||||
Currently Unsupported Queryset Methods
|
Currently Unsupported Queryset Methods
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
+ aggregate() probably makes only sense in a purely non-OO/relational
|
+ ``aggregate()`` probably makes only sense in a purely non-OO/relational
|
||||||
way. So it seems an implementation would just fall back to the
|
way. So it seems an implementation would just fall back to the
|
||||||
Django vanilla equivalent.
|
Django vanilla equivalent.
|
||||||
|
|
||||||
+ annotate(): The current '_get_real_instances' would need minor
|
+ ``annotate()``: The current '_get_real_instances' would need minor
|
||||||
enhancement.
|
enhancement.
|
||||||
|
|
||||||
+ defer() and only(): Full support, including slight polymorphism
|
+ ``defer()`` and ``only()``: Full support, including slight polymorphism
|
||||||
enhancements, seems to be straighforward
|
enhancements, seems to be straighforward
|
||||||
(depends on '_get_real_instances').
|
(depends on '_get_real_instances').
|
||||||
|
|
||||||
+ extra(): Does not really work with the current implementation of
|
+ ``extra()``: Does not really work with the current implementation of
|
||||||
'_get_real_instances'. It's unclear if it should be supported.
|
'_get_real_instances'. It's unclear if it should be supported.
|
||||||
|
|
||||||
+ select_related(): This would probably need Django core support
|
+ ``select_related()``: This would probably need Django core support
|
||||||
for traversing the reverse model inheritance OneToOne relations
|
for traversing the reverse model inheritance OneToOne relations
|
||||||
with Django's select_related(), e.g.:
|
with Django's select_related(), e.g.:
|
||||||
*select_related('modela__modelb__foreignkeyfield')*.
|
``select_related('modela__modelb__foreignkeyfield')``.
|
||||||
Also needs more thought/investigation.
|
Also needs more thought/investigation.
|
||||||
|
|
||||||
+ distinct() needs more thought and investigation as well
|
+ ``distinct()`` needs more thought and investigation as well
|
||||||
|
|
||||||
+ values() & values_list(): Implementation seems to be mostly
|
+ ``values()`` & ``values_list()``: Implementation seems to be mostly
|
||||||
straighforward
|
straighforward
|
||||||
|
|
||||||
|
|
||||||
Restrictions & Caveats
|
Restrictions & Caveats
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
@ -380,12 +371,11 @@ Restrictions & Caveats
|
||||||
by subclassing it instead of modifying Django core (as we do here
|
by subclassing it instead of modifying Django core (as we do here
|
||||||
with PolymorphicModel).
|
with PolymorphicModel).
|
||||||
|
|
||||||
+ The name and appname of the leaf model is stored in the base model
|
+ A reference (``ContentType``) to the real/leaf model is stored
|
||||||
(the base model directly inheriting from PolymorphicModel).
|
in the base model (the base model directly inheriting from
|
||||||
If a model or an app is renamed, then these fields need to be
|
PolymorphicModel). If a model or an app is renamed, then Django's
|
||||||
corrected too, if the db content should stay usable after the rename.
|
ContentType table needs to be corrected too, if the db content
|
||||||
Aside from this, these two fields should probably be combined into
|
should stay usable after the rename.
|
||||||
one field (more db/sql efficiency)
|
|
||||||
|
|
||||||
+ For all objects that are not instances of the base class type, but
|
+ For all objects that are not instances of the base class type, but
|
||||||
instances of a subclass, the base class fields are currently
|
instances of a subclass, the base class fields are currently
|
||||||
|
|
@ -396,9 +386,9 @@ Restrictions & Caveats
|
||||||
fields (like basemodel_ptr), as well as implicit model inheritance
|
fields (like basemodel_ptr), as well as implicit model inheritance
|
||||||
forward relation fields, Django internally tries to use our
|
forward relation fields, Django internally tries to use our
|
||||||
polymorphic manager/queryset in some places, which of course it
|
polymorphic manager/queryset in some places, which of course it
|
||||||
should not. Currently this is solved with hackish __getattribute__
|
should not. Currently this is solved with a hacky __getattribute__
|
||||||
in PolymorphicModel. A minor patch to Django core would probably
|
in PolymorphicModel, which causes some overhead. A minor patch t
|
||||||
get rid of that.
|
Django core would probably get rid of that.
|
||||||
|
|
||||||
In General
|
In General
|
||||||
----------
|
----------
|
||||||
|
|
|
||||||
52
README.rst
52
README.rst
|
|
@ -1,20 +1,30 @@
|
||||||
===============================
|
**2010-1-26**
|
||||||
Fully Polymorphic Django Models
|
IMPORTANT - database schema change (more info in change log).
|
||||||
===============================
|
|
||||||
|
|
||||||
News
|
|
||||||
----
|
|
||||||
|
|
||||||
* 2010-1-26: IMPORTANT - database schema change (more info in change log).
|
|
||||||
I hope I got this change in early enough before anyone started to use
|
I hope I got this change in early enough before anyone started to use
|
||||||
polymorphic.py in earnest. Sorry for any inconvenience.
|
polymorphic.py in earnest. Sorry for any inconvenience.
|
||||||
This should be the final DB schema now!
|
This should be the final DB schema now.
|
||||||
|
|
||||||
|
|
||||||
|
Usage, Examples, Installation & Documentation, Links
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
* Documentation_ and Overview_
|
||||||
|
* `Discussion, Questions, Suggestions`_
|
||||||
|
* GitHub_ - Bitbucket_ - `Download as TGZ`_ or ZIP_
|
||||||
|
|
||||||
|
.. _Documentation: http://bserve.webhop.org/wiki/django_polymorphic/doc
|
||||||
|
.. _Discussion, Questions, Suggestions: http://django-polymorphic.blogspot.com/2010/01/messages.html
|
||||||
|
.. _GitHub: http://github.com/bconstantin/django_polymorphic
|
||||||
|
.. _Bitbucket: http://bitbucket.org/bconstantin/django_polymorphic
|
||||||
|
.. _Download as TGZ: http://github.com/bconstantin/django_polymorphic/tarball/master
|
||||||
|
.. _ZIP: http://github.com/bconstantin/django_polymorphic/zipball/master
|
||||||
|
.. _Overview: http://bserve.webhop.org/wiki/django_polymorphic
|
||||||
|
|
||||||
|
|
||||||
What is django_polymorphic good for?
|
What is django_polymorphic good for?
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
If ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``::
|
If ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``:
|
||||||
|
|
||||||
>>> Project.objects.all()
|
>>> Project.objects.all()
|
||||||
.
|
.
|
||||||
|
|
@ -22,26 +32,24 @@ If ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``::
|
||||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
||||||
|
|
||||||
In general, objects retrieved from the database are always delivered just as
|
In general, objects retrieved from the database are always returned back
|
||||||
they were created and saved, with the same type/class and fields. It doesn't
|
with the same type/class and fields they were created and saved with.
|
||||||
matter how you access these objects: be it through the model's own
|
It doesn't matter how these objects are retrieved: be it through the
|
||||||
managers/querysets, ForeignKey, ManyToMany or OneToOne fields.
|
model's own managers/querysets, ForeignKeys, ManyToManyFields
|
||||||
|
or OneToOneFields.
|
||||||
|
|
||||||
The resulting querysets are polymorphic, and may deliver
|
The resulting querysets are polymorphic, i.e they may deliver
|
||||||
objects of several different types in a single query result.
|
objects of several different types in a single query result.
|
||||||
|
|
||||||
``django_polymorphic`` consists of just one add-on module, ``polymorphic.py``,
|
``django_polymorphic`` consists of just one add-on module, ``polymorphic.py``,
|
||||||
that adds this kind of automatic polymorphism to Django's model
|
that adds this functionality to Django's model inheritance system
|
||||||
inheritance system (for models that request this behaviour).
|
(for models that request this behaviour).
|
||||||
|
|
||||||
Please see additional examples and the documentation here:
|
|
||||||
|
|
||||||
http://bserve.webhop.org/wiki/django_polymorphic
|
|
||||||
|
|
||||||
or in the DOCS.rst file in this repository.
|
|
||||||
|
|
||||||
Status
|
Status
|
||||||
------
|
------
|
||||||
|
|
||||||
This module is still very experimental. Please see the docs for current restrictions,
|
This module is still very experimental. Please see the docs for current restrictions,
|
||||||
caveats, and performance implications.
|
caveats, and performance implications.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ Please see the examples and documentation here:
|
||||||
or in the included README.rst and DOCS.rst files.
|
or in the included README.rst and DOCS.rst files.
|
||||||
|
|
||||||
Copyright:
|
Copyright:
|
||||||
This code and affiliated files are (C) by
|
This code and affiliated files are (C) by Bert Constantin and individual contributors.
|
||||||
Bert Constantin and the individual contributors.
|
|
||||||
Please see LICENSE and AUTHORS for more information.
|
Please see LICENSE and AUTHORS for more information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -21,6 +20,7 @@ from django.db.models.query import QuerySet
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
import sys
|
||||||
|
|
||||||
# chunk-size: maximum number of objects requested per db-request
|
# chunk-size: maximum number of objects requested per db-request
|
||||||
# by the polymorphic queryset.iterator() implementation
|
# by the polymorphic queryset.iterator() implementation
|
||||||
|
|
@ -48,7 +48,7 @@ class PolymorphicManager(models.Manager):
|
||||||
return self.queryset_class(self.model)
|
return self.queryset_class(self.model)
|
||||||
|
|
||||||
# Proxy all unknown method calls to the queryset, so that its members are
|
# Proxy all unknown method calls to the queryset, so that its members are
|
||||||
# directly accessible from PolymorphicModel.objects.
|
# directly accessible as PolymorphicModel.objects.*
|
||||||
# The advantage is that not yet known member functions of derived querysets will be proxied as well.
|
# The advantage is that not yet known member functions of derived querysets will be proxied as well.
|
||||||
# We exclude any special functions (__) from this automatic proxying.
|
# We exclude any special functions (__) from this automatic proxying.
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
|
|
@ -109,8 +109,8 @@ class PolymorphicQuerySet(QuerySet):
|
||||||
ordered_id_list = [] # list of ids of result-objects in correct order
|
ordered_id_list = [] # list of ids of result-objects in correct order
|
||||||
results = {} # polymorphic dict of result-objects, keyed with their id (no order)
|
results = {} # polymorphic dict of result-objects, keyed with their id (no order)
|
||||||
|
|
||||||
# dict contains one entry for the different model types occurring in result,
|
# dict contains one entry per unique model type occurring in result,
|
||||||
# in the format idlist_per_model['applabel.modelname']=[list-of-ids-for-this-model]
|
# in the format idlist_per_model[modelclass]=[list-of-object-ids]
|
||||||
idlist_per_model = defaultdict(list)
|
idlist_per_model = defaultdict(list)
|
||||||
|
|
||||||
# - sort base_result_object ids into idlist_per_model lists, depending on their real class;
|
# - sort base_result_object ids into idlist_per_model lists, depending on their real class;
|
||||||
|
|
@ -307,7 +307,9 @@ def _translate_polymorphic_field_path(queryset_model, field_path):
|
||||||
if issubclass(model, models.Model) and model != models.Model:
|
if issubclass(model, models.Model) and model != models.Model:
|
||||||
# model name is occurring twice in submodel inheritance tree => Error
|
# model name is occurring twice in submodel inheritance tree => Error
|
||||||
if model.__name__ in result and model != result[model.__name__]:
|
if model.__name__ in result and model != result[model.__name__]:
|
||||||
assert model, 'PolymorphicModel: model name is ambiguous: %s.%s, %s.%s!' % (
|
e = 'PolymorphicModel: model name alone is ambiguous: %s.%s and %s.%s!\n'
|
||||||
|
e += 'In this case, please use the syntax: applabel__ModelName___field'
|
||||||
|
assert model, e % (
|
||||||
model._meta.app_label, model.__name__,
|
model._meta.app_label, model.__name__,
|
||||||
result[model.__name__]._meta.app_label, result[model.__name__].__name__)
|
result[model.__name__]._meta.app_label, result[model.__name__].__name__)
|
||||||
|
|
||||||
|
|
@ -451,6 +453,7 @@ class PolymorphicModelBase(ModelBase):
|
||||||
add_managers_keys.add(key)
|
add_managers_keys.add(key)
|
||||||
return add_managers
|
return add_managers
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_first_user_defined_manager(self, attrs):
|
def get_first_user_defined_manager(self, attrs):
|
||||||
mgr_list = []
|
mgr_list = []
|
||||||
|
|
@ -617,7 +620,7 @@ class ShowFields(object):
|
||||||
else:
|
else:
|
||||||
out += ': "' + getattr(self, f.name) + '"'
|
out += ': "' + getattr(self, f.name) + '"'
|
||||||
if f != last: out += ', '
|
if f != last: out += ', '
|
||||||
return '<' + (self.__class__.__name__ + ': ').ljust(17) + out + '>'
|
return '<' + (self.__class__.__name__ + ': ') + out + '>'
|
||||||
|
|
||||||
|
|
||||||
class ShowFieldsAndTypes(object):
|
class ShowFieldsAndTypes(object):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue