doc- and minor code updates
parent
c7ac78e08d
commit
2a740e9b10
97
DOCS.rst
97
DOCS.rst
|
|
@ -9,8 +9,6 @@ Requirements
|
||||||
|
|
||||||
Django 1.1 (or later) and Python 2.5 (or later). This code has been tested
|
Django 1.1 (or later) and Python 2.5 (or later). This code has been tested
|
||||||
on Django 1.1.1 / 1.2 alpha and Python 2.5.4 / 2.6.4 on Linux.
|
on Django 1.1.1 / 1.2 alpha and Python 2.5.4 / 2.6.4 on Linux.
|
||||||
Django's ContentType framework is used (part of Django).
|
|
||||||
|
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
-------
|
-------
|
||||||
|
|
@ -32,16 +30,14 @@ to your liking, then run::
|
||||||
Using polymorphic models in your own projects
|
Using polymorphic models in your own projects
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
The best way for now is probably to just copy the ``polymorphic`` directory
|
The easiest way for now is to just copy the ``polymorphic`` directory
|
||||||
into your project dir. If you want to use the management command
|
(under ``django_polymorphic``) into your Django project dir.
|
||||||
``polymorphic_dumpdata``, then you also need to add ``polymorphic``
|
|
||||||
to your INSTALLED_APPS setting. The ContentType framework
|
|
||||||
(``django.contrib.contenttypes``) needs to be listed in INSTALLED_APPS
|
|
||||||
as well (usually it already is).
|
|
||||||
|
|
||||||
It's also still possible to use ``polymorphic.py`` only, as a single file
|
If you want to use the management command ``polymorphic_dumpdata``, then
|
||||||
add-on module, copied to somewhere where it can be imported (like your
|
you need to add ``polymorphic`` to your INSTALLED_APPS setting as well.
|
||||||
own app dir).
|
|
||||||
|
In any case, the ContentType framework (``django.contrib.contenttypes``)
|
||||||
|
needs to be listed in INSTALLED_APPS (usually it already is).
|
||||||
|
|
||||||
|
|
||||||
Defining Polymorphic Models
|
Defining Polymorphic Models
|
||||||
|
|
@ -193,8 +189,14 @@ manage.py dumpdata
|
||||||
Django's standard ``dumpdata`` command requires non-polymorphic
|
Django's standard ``dumpdata`` command requires non-polymorphic
|
||||||
behaviour from the querysets it uses and produces incomplete
|
behaviour from the querysets it uses and produces incomplete
|
||||||
results with polymorphic models. Django_polymorphic includes
|
results with polymorphic models. Django_polymorphic includes
|
||||||
a slightly modified version, named ``polymorphic_dumpdata``.
|
a slightly modified version, named ``polymorphic_dumpdata``
|
||||||
Just use this command instead of Django's (see "installation/testing").
|
that fixes this. Just use this command instead of Django's
|
||||||
|
(see "installation/testing").
|
||||||
|
|
||||||
|
Please note that there are problems using ContentType together
|
||||||
|
with Django's seralisation or fixtures (and all polymorphic models
|
||||||
|
use ContentType). This issue seems to be resolved with Django 1.2
|
||||||
|
(changeset 11863): http://code.djangoproject.com/ticket/7052
|
||||||
|
|
||||||
|
|
||||||
Custom Managers, Querysets & Inheritance
|
Custom Managers, Querysets & Inheritance
|
||||||
|
|
@ -221,12 +223,12 @@ 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
|
||||||
---------------------------------
|
-------------------
|
||||||
|
|
||||||
Polymorphic models unconditionally propagate/inherit all managers from
|
Polymorphic models inherit/propagate all managers from their
|
||||||
their base models, as long as these are polymorphic. This means that all
|
base models, as long as these are polymorphic. This means that all
|
||||||
managers inherited from polymorphic base models work just the same as if
|
managers defined in polymorphic base models work just the same as if
|
||||||
they were defined in the new model.
|
they were defined in the new model.
|
||||||
|
|
||||||
An example (inheriting from MyModel above)::
|
An example (inheriting from MyModel above)::
|
||||||
|
|
@ -332,18 +334,18 @@ 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.
|
||||||
|
|
||||||
General
|
In General
|
||||||
-------------------
|
----------
|
||||||
|
|
||||||
Let's not forget that all of the above is just about optimization.
|
Let's not forget that all of the above is just about optimization.
|
||||||
The current implementation already works well - and perhaps well
|
The current implementation already works well - and perhaps well
|
||||||
enough for the majority of applications.
|
enough for the majority of applications.
|
||||||
|
|
||||||
Also, it seems that further optimization (down to one DB request)
|
Also, it seems that further optimization (down to one DB request)
|
||||||
would be restricted to a small area of the code, straightforward
|
would be restricted to a relatively small area of the code, and
|
||||||
to implement, and mostly independent from the rest of the module.
|
be mostly independent from the rest of the module.
|
||||||
So this optimization can be done at any later time (like when
|
So it seems this optimization can be done at any later time
|
||||||
it's needed).
|
(like when it's needed).
|
||||||
|
|
||||||
|
|
||||||
Unsupported Methods, Restrictions & Caveats
|
Unsupported Methods, Restrictions & Caveats
|
||||||
|
|
@ -360,26 +362,31 @@ Currently Unsupported Queryset Methods
|
||||||
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()`` works just as usual, but it can not (yet) be used
|
||||||
for traversing the reverse model inheritance OneToOne relations
|
to select relations in derived models
|
||||||
with Django's select_related(), e.g.:
|
(like ``ModelA.objects.select_related('ModelC___fieldxy')`` )
|
||||||
``select_related('modela__modelb__foreignkeyfield')``.
|
|
||||||
Also needs more thought/investigation.
|
|
||||||
|
|
||||||
+ ``distinct()`` needs more thought and investigation as well
|
+ ``distinct()`` needs more thought and investigation
|
||||||
|
|
||||||
|
+ ``values()`` & ``values_list()``: Implementation seems to be mostly straighforward
|
||||||
|
|
||||||
+ ``values()`` & ``values_list()``: Implementation seems to be mostly
|
|
||||||
straighforward
|
|
||||||
|
|
||||||
Restrictions & Caveats
|
Restrictions & Caveats
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
* ``Django 1.1 only``: When ContentType is used in models, Django's
|
||||||
|
seralisation or fixtures cannot be used. This issue seems to be
|
||||||
|
resolved for Django 1.2 (changeset 11863: Fixed #7052,
|
||||||
|
Added support for natural keys in serialization).
|
||||||
|
|
||||||
|
+ http://code.djangoproject.com/ticket/7052
|
||||||
|
+ http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django
|
||||||
|
|
||||||
* Diamond shaped inheritance: There seems to be a general problem
|
* Diamond shaped inheritance: There seems to be a general problem
|
||||||
with diamond shaped multiple model inheritance with Django models
|
with diamond shaped multiple model inheritance with Django models
|
||||||
(tested with V1.1).
|
(tested with V1.1).
|
||||||
|
|
@ -394,13 +401,6 @@ Restrictions & Caveats
|
||||||
ContentType table needs to be corrected too, if the db content
|
ContentType table needs to be corrected too, if the db content
|
||||||
should stay usable after the rename.
|
should stay usable after the rename.
|
||||||
|
|
||||||
* The use of ContentType together with Django's seralisation or
|
|
||||||
fixtures seems to pose problems up to Django 1.1. 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
|
|
||||||
|
|
||||||
* For all objects that are not instances of the base class, but
|
* For all objects that are not instances of the base class, but
|
||||||
instances of a subclass, the base class fields are currently
|
instances of a subclass, the base class fields are currently
|
||||||
transferred twice from the database (an artefact of the current
|
transferred twice from the database (an artefact of the current
|
||||||
|
|
@ -411,19 +411,20 @@ Restrictions & Caveats
|
||||||
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 a hacky __getattribute__
|
should not. Currently this is solved with a hacky __getattribute__
|
||||||
in PolymorphicModel, which causes some overhead. A minor patch t
|
in PolymorphicModel, which causes some overhead. A minor patch to
|
||||||
Django core would probably get rid of that.
|
Django core would probably get rid of that.
|
||||||
|
|
||||||
In General
|
In General
|
||||||
----------
|
----------
|
||||||
|
|
||||||
It's important to consider that this code is still very new and experimental.
|
It's important to consider that this code is very new and
|
||||||
|
to some extent still experimental. Please see the docs for
|
||||||
|
current restrictions, caveats, and performance implications.
|
||||||
|
|
||||||
Right now it's suitable only for the more enterprising early adopters.
|
It does seem to work very well for a number of people, but
|
||||||
|
API changes, code reorganisations or further schema changes
|
||||||
It does seem to work well for a number of people (including me), but
|
are still a possibility. There may also remain larger bugs
|
||||||
it's still very early and API changes, code reorganisations or further
|
and problems in the code that have not yet been found.
|
||||||
schema changes are still a possibility.
|
|
||||||
|
|
||||||
|
|
||||||
Links
|
Links
|
||||||
|
|
|
||||||
71
README.rst
71
README.rst
|
|
@ -1,19 +1,12 @@
|
||||||
**2010-1-26**
|
Release Notes, Usage, Code
|
||||||
IMPORTANT - database schema change (more info in change log).
|
--------------------------
|
||||||
I hope I got this change in early enough before anyone started to use
|
|
||||||
polymorphic.py in earnest. Sorry for any inconvenience.
|
|
||||||
This should be the final DB schema now.
|
|
||||||
|
|
||||||
|
* Please see `here for release notes, news and discussion`_ (Google Group)
|
||||||
|
* Installation and usage: `Documentation and Examples`_ (or the short `Overview`_)
|
||||||
|
* The code is on GitHub_ and Bitbucket_ and can also be downloaded as TGZ_ or ZIP_
|
||||||
|
|
||||||
Usage, Examples, Installation & Documentation, Links
|
.. _here for release notes, news and discussion: http://groups.google.de/group/django-polymorphic/topics
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
* Please see the `Documentation and Examples`_ (or the short `Overview`_)
|
|
||||||
* News & comments: `News, Comments, Questions & Discussion`_
|
|
||||||
* The code can be found on GitHub_ and Bitbucket_, or downloaded as TGZ_ or ZIP_
|
|
||||||
|
|
||||||
.. _Documentation and Examples: http://bserve.webhop.org/wiki/django_polymorphic/doc
|
.. _Documentation and Examples: http://bserve.webhop.org/wiki/django_polymorphic/doc
|
||||||
.. _News, Comments, Questions & Discussion: http://groups.google.de/group/django-polymorphic/topics
|
|
||||||
.. _GitHub: http://github.com/bconstantin/django_polymorphic
|
.. _GitHub: http://github.com/bconstantin/django_polymorphic
|
||||||
.. _Bitbucket: http://bitbucket.org/bconstantin/django_polymorphic
|
.. _Bitbucket: http://bitbucket.org/bconstantin/django_polymorphic
|
||||||
.. _TGZ: http://github.com/bconstantin/django_polymorphic/tarball/master
|
.. _TGZ: http://github.com/bconstantin/django_polymorphic/tarball/master
|
||||||
|
|
@ -24,12 +17,15 @@ Usage, Examples, Installation & Documentation, Links
|
||||||
What is django_polymorphic good for?
|
What is django_polymorphic good for?
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
It causes objects being retrieved from the database to always be returned back
|
It implements seamless polymorphic inheritance for Django models.
|
||||||
with the same type/class and fields they were created and saved with.
|
|
||||||
|
|
||||||
Example:
|
This means: objects being retrieved from the database are always returned
|
||||||
If the models ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``,
|
back with the same type/class and fields they were created and saved with.
|
||||||
and we have saved one of each of them into the database, then we can do::
|
|
||||||
|
An example:
|
||||||
|
If we defined the model ``Project`` as a base class for our models
|
||||||
|
``ArtProject`` and ``ResearchProject``, and we have stored one of
|
||||||
|
each into the database, then we can do::
|
||||||
|
|
||||||
>>> Project.objects.all()
|
>>> Project.objects.all()
|
||||||
.
|
.
|
||||||
|
|
@ -41,23 +37,40 @@ It doesn't matter how these objects are retrieved: be it through the
|
||||||
model's own managers/querysets, ForeignKeys, ManyToManyFields
|
model's own managers/querysets, ForeignKeys, ManyToManyFields
|
||||||
or OneToOneFields.
|
or OneToOneFields.
|
||||||
|
|
||||||
``django_polymorphic`` does this only for models that explicitly request
|
As seen in this example, the resulting querysets are polymorphic,
|
||||||
this behaviour (and implicitely for their submodels).
|
and will typically deliver objects of several different types in
|
||||||
|
a single query result.
|
||||||
|
|
||||||
The resulting querysets are polymorphic, i.e they may deliver
|
django_polymorphic does this only for models that explicitely enable it
|
||||||
objects of several different types in a single query result.
|
(and for their submodels).
|
||||||
|
|
||||||
|
Please see the `Documentation and Examples`_ for more information
|
||||||
|
(also included as the file ``DOCS.rst`` with the source).
|
||||||
|
|
||||||
|
|
||||||
Status
|
Status
|
||||||
------
|
------
|
||||||
|
|
||||||
It's important to consider that this code is still very new and
|
It's important to consider that this code is very new and
|
||||||
experimental. Please see the docs for current restrictions, caveats,
|
to some extent still experimental. Please see the docs for
|
||||||
and performance implications.
|
current restrictions, caveats, and performance implications.
|
||||||
|
|
||||||
It does seem to work well for a number of people (including me), but
|
It does seem to work very well for a number of people, but
|
||||||
it's still very early and API changes, code reorganisations or further
|
API changes, code reorganisations or further schema changes
|
||||||
schema changes are still a possibility.
|
are still a possibility. There may also remain larger bugs
|
||||||
|
and problems in the code that have not yet been found.
|
||||||
|
|
||||||
Right now it's suitable only for the more enterprising early adopters.
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
django_polymorphic uses the same license as Django (BSD-like).
|
||||||
|
|
||||||
|
|
||||||
|
Database Schema Change on January 26
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
| 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
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,11 @@ class ModelB(ModelA):
|
||||||
field2 = models.CharField(max_length=10)
|
field2 = models.CharField(max_length=10)
|
||||||
class ModelC(ModelB):
|
class ModelC(ModelB):
|
||||||
field3 = models.CharField(max_length=10)
|
field3 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class SModelA(ShowFieldsAndTypes, PolymorphicModel):
|
||||||
|
field1 = models.CharField(max_length=10)
|
||||||
|
class SModelB(SModelA):
|
||||||
|
field2 = models.CharField(max_length=10)
|
||||||
|
class SModelC(SModelB):
|
||||||
|
field3 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Fully Polymorphic Django Models
|
Fully Polymorphic Django Models
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
Please see the examples and documentation here:
|
For an overview, examples, documentation and updates please see here:
|
||||||
|
|
||||||
http://bserve.webhop.org/wiki/django_polymorphic
|
http://bserve.webhop.org/wiki/django_polymorphic
|
||||||
|
|
||||||
|
|
@ -62,6 +62,10 @@ class PolymorphicManager(models.Manager):
|
||||||
###################################################################################
|
###################################################################################
|
||||||
### PolymorphicQuerySet
|
### PolymorphicQuerySet
|
||||||
|
|
||||||
|
# PolymorphicQuerySet Q objects (and filter()) support these additional key words.
|
||||||
|
# These are forbidden as field names (a descriptive exception is raised)
|
||||||
|
POLYMORPHIC_SPECIAL_Q_KWORDS = [ 'instance_of', 'not_instance_of']
|
||||||
|
|
||||||
class PolymorphicQuerySet(QuerySet):
|
class PolymorphicQuerySet(QuerySet):
|
||||||
"""
|
"""
|
||||||
QuerySet for PolymorphicModel
|
QuerySet for PolymorphicModel
|
||||||
|
|
@ -73,15 +77,19 @@ class PolymorphicQuerySet(QuerySet):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def instance_of(self, *args):
|
def instance_of(self, *args):
|
||||||
|
"""Filter the queryset to only include the classes in args (and their subclasses).
|
||||||
|
Implementation in _translate_polymorphic_filter_defnition."""
|
||||||
return self.filter(instance_of=args)
|
return self.filter(instance_of=args)
|
||||||
|
|
||||||
def not_instance_of(self, *args):
|
def not_instance_of(self, *args):
|
||||||
|
"""Filter the queryset to exclude the classes in args (and their subclasses).
|
||||||
|
Implementation in _translate_polymorphic_filter_defnition."""
|
||||||
return self.filter(not_instance_of=args)
|
return self.filter(not_instance_of=args)
|
||||||
|
|
||||||
def _filter_or_exclude(self, negate, *args, **kwargs):
|
def _filter_or_exclude(self, negate, *args, **kwargs):
|
||||||
""" we override this internal Django functon since it is used for all filtering """
|
"We override this internal Django functon as it is used for all filter member functions."
|
||||||
_translate_polymorphic_filter_defnitions_in_args(self.model, args) # the Q objects
|
_translate_polymorphic_filter_definitions_in_args(self.model, args) # the Q objects
|
||||||
additional_args = _translate_polymorphic_filter_defnitions_in_kwargs(self.model, kwargs) # filter_field='data'
|
additional_args = _translate_polymorphic_filter_definitions_in_kwargs(self.model, kwargs) # filter_field='data'
|
||||||
return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(args) + additional_args), **kwargs)
|
return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(args) + additional_args), **kwargs)
|
||||||
|
|
||||||
# these queryset functions are not yet supported
|
# these queryset functions are not yet supported
|
||||||
|
|
@ -154,13 +162,13 @@ class PolymorphicQuerySet(QuerySet):
|
||||||
def iterator(self):
|
def iterator(self):
|
||||||
"""
|
"""
|
||||||
This function is used by Django for all object retrieval.
|
This function is used by Django for all object retrieval.
|
||||||
By overriding it, we modify the objects that ths queryset returns
|
By overriding it, we modify the objects that this queryset returns
|
||||||
when it is evaluated (or it's get method or other object-returning methods are called).
|
when it is evaluated (or its get method or other object-returning methods are called).
|
||||||
|
|
||||||
Here we do the same as:
|
Here we do the same as:
|
||||||
|
|
||||||
base_result_objects=list(super(PolymorphicQuerySet, self).iterator())
|
base_result_objects=list(super(PolymorphicQuerySet, self).iterator())
|
||||||
real_results=self._get_get_real_instances(base_result_objects)
|
real_results=self._get_real_instances(base_result_objects)
|
||||||
for o in real_results: yield o
|
for o in real_results: yield o
|
||||||
|
|
||||||
but it requests the objects in chunks from the database,
|
but it requests the objects in chunks from the database,
|
||||||
|
|
@ -196,9 +204,9 @@ class PolymorphicQuerySet(QuerySet):
|
||||||
# These functions implement the additional filter- and Q-object functionality.
|
# These functions implement the additional filter- and Q-object functionality.
|
||||||
# They form a kind of small framework for easily adding more
|
# They form a kind of small framework for easily adding more
|
||||||
# functionality to filters and Q objects.
|
# functionality to filters and Q objects.
|
||||||
# Probably a more general queryset enhancement class could be made out them.
|
# Probably a more general queryset enhancement class could be made out of them.
|
||||||
|
|
||||||
def _translate_polymorphic_filter_defnitions_in_kwargs(queryset_model, kwargs):
|
def _translate_polymorphic_filter_definitions_in_kwargs(queryset_model, kwargs):
|
||||||
"""
|
"""
|
||||||
Translate the keyword argument list for PolymorphicQuerySet.filter()
|
Translate the keyword argument list for PolymorphicQuerySet.filter()
|
||||||
|
|
||||||
|
|
@ -214,8 +222,9 @@ def _translate_polymorphic_filter_defnitions_in_kwargs(queryset_model, kwargs):
|
||||||
"""
|
"""
|
||||||
additional_args = []
|
additional_args = []
|
||||||
for field_path, val in kwargs.items():
|
for field_path, val in kwargs.items():
|
||||||
# normal filter expression => ignore
|
|
||||||
new_expr = _translate_polymorphic_filter_defnition(queryset_model, field_path, val)
|
new_expr = _translate_polymorphic_filter_defnition(queryset_model, field_path, val)
|
||||||
|
|
||||||
if type(new_expr) == tuple:
|
if type(new_expr) == tuple:
|
||||||
# replace kwargs element
|
# replace kwargs element
|
||||||
del(kwargs[field_path])
|
del(kwargs[field_path])
|
||||||
|
|
@ -227,7 +236,7 @@ def _translate_polymorphic_filter_defnitions_in_kwargs(queryset_model, kwargs):
|
||||||
|
|
||||||
return additional_args
|
return additional_args
|
||||||
|
|
||||||
def _translate_polymorphic_filter_defnitions_in_args(queryset_model, args):
|
def _translate_polymorphic_filter_definitions_in_args(queryset_model, args):
|
||||||
"""
|
"""
|
||||||
Translate the non-keyword argument list for PolymorphicQuerySet.filter()
|
Translate the non-keyword argument list for PolymorphicQuerySet.filter()
|
||||||
|
|
||||||
|
|
@ -288,7 +297,7 @@ def _translate_polymorphic_filter_defnition(queryset_model, field_path, field_va
|
||||||
|
|
||||||
def _translate_polymorphic_field_path(queryset_model, field_path):
|
def _translate_polymorphic_field_path(queryset_model, field_path):
|
||||||
"""
|
"""
|
||||||
Translate a field path from keyword argument, as used for
|
Translate a field path from a keyword argument, as used for
|
||||||
PolymorphicQuerySet.filter()-like functions (and Q objects).
|
PolymorphicQuerySet.filter()-like functions (and Q objects).
|
||||||
|
|
||||||
E.g.: ModelC___field3 is translated into modela__modelb__modelc__field3
|
E.g.: ModelC___field3 is translated into modela__modelb__modelc__field3
|
||||||
|
|
@ -416,6 +425,9 @@ class PolymorphicModelBase(ModelBase):
|
||||||
# create new model
|
# create new model
|
||||||
new_class = self.call_superclass_new_method(model_name, bases, attrs)
|
new_class = self.call_superclass_new_method(model_name, bases, attrs)
|
||||||
|
|
||||||
|
# check if the model fields are all allowed
|
||||||
|
self.validate_model_fields(new_class)
|
||||||
|
|
||||||
# create list of all managers to be inherited from the base classes
|
# create list of all managers to be inherited from the base classes
|
||||||
inherited_managers = new_class.get_inherited_managers(attrs)
|
inherited_managers = new_class.get_inherited_managers(attrs)
|
||||||
|
|
||||||
|
|
@ -494,6 +506,13 @@ class PolymorphicModelBase(ModelBase):
|
||||||
if do_app_label_workaround: del(meta.app_label)
|
if do_app_label_workaround: del(meta.app_label)
|
||||||
return new_class
|
return new_class
|
||||||
|
|
||||||
|
def validate_model_fields(self):
|
||||||
|
"check if all fields names are allowed (i.e. not in POLYMORPHIC_SPECIAL_Q_KWORDS)"
|
||||||
|
for f in self._meta.fields:
|
||||||
|
if f.name in POLYMORPHIC_SPECIAL_Q_KWORDS:
|
||||||
|
e = 'PolymorphicModel: "%s" - field name "%s" is not allowed in polymorphic models'
|
||||||
|
raise AssertionError(e % (self.__name__, f.name) )
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_model_manager(self, manager, model_name, manager_name):
|
def validate_model_manager(self, manager, model_name, manager_name):
|
||||||
"""check if the manager is derived from PolymorphicManager
|
"""check if the manager is derived from PolymorphicManager
|
||||||
|
|
@ -638,7 +657,7 @@ class PolymorphicModel(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class ShowFields(object):
|
class ShowFields(object):
|
||||||
""" mixin that shows the object's class, it's fields and field contents """
|
""" model mixin that shows the object's class, it's fields and field contents """
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
out = 'id %d, ' % (self.id); last = self._meta.fields[-1]
|
out = 'id %d, ' % (self.id); last = self._meta.fields[-1]
|
||||||
for f in self._meta.fields:
|
for f in self._meta.fields:
|
||||||
|
|
@ -654,7 +673,7 @@ class ShowFields(object):
|
||||||
|
|
||||||
|
|
||||||
class ShowFieldsAndTypes(object):
|
class ShowFieldsAndTypes(object):
|
||||||
""" like ShowFields, but also show field types """
|
""" model mixin, like ShowFields, but also show field types """
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
out = 'id %d, ' % (self.id); last = self._meta.fields[-1]
|
out = 'id %d, ' % (self.id); last = self._meta.fields[-1]
|
||||||
for f in self._meta.fields:
|
for f in self._meta.fields:
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,10 @@ class MgrInheritB(MgrInheritA):
|
||||||
class MgrInheritC(ShowFieldsAndTypes, MgrInheritB):
|
class MgrInheritC(ShowFieldsAndTypes, MgrInheritB):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# test bad field name
|
||||||
|
#class TestBadFieldModel(PolymorphicModel):
|
||||||
|
# instance_of = models.CharField(max_length=10)
|
||||||
|
|
||||||
# validation error: "polymorphic.relatednameclash: Accessor for field 'polymorphic_ctype' clashes
|
# validation error: "polymorphic.relatednameclash: Accessor for field 'polymorphic_ctype' clashes
|
||||||
# with related field 'ContentType.relatednameclash_set'." (reported by Andrew Ingram)
|
# with related field 'ContentType.relatednameclash_set'." (reported by Andrew Ingram)
|
||||||
# fixed with related_name
|
# fixed with related_name
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue