Restructured django_polymorphic into a regular add-on application.
This is needed for the management commands, and also seems to be a generally good idea for future viablity as well. Also misc documentation updates.fix_request_path_info
parent
dd84e911d0
commit
8bdda93e76
63
DOCS.rst
63
DOCS.rst
|
|
@ -9,6 +9,8 @@ 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
|
||||||
-------
|
-------
|
||||||
|
|
@ -18,20 +20,28 @@ that may be used for tests or experiments.
|
||||||
|
|
||||||
To run the included test suite, execute::
|
To run the included test suite, execute::
|
||||||
|
|
||||||
./manage test poly
|
./manage test polymorphic
|
||||||
|
|
||||||
'management/commands/polycmd.py' can be used for experiments
|
The management command ``pcmd.py`` in the app ``pexp`` (Polymorphic EXPerimenting)
|
||||||
- modify this file to your liking, then run::
|
can be used for experiments - modify this file (pexp/management/commands/pcmd.py)
|
||||||
|
to your liking, then run::
|
||||||
|
|
||||||
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
./manage syncdb # db is created in /var/tmp/... (settings.py)
|
||||||
./manage polycmd
|
./manage pcmd
|
||||||
|
|
||||||
Using polymorphic models in your own projects
|
Using polymorphic models in your own projects
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
Copy polymorphic.py (from the 'poly' dir) into a directory from where
|
The best way for now is probably to just copy the ``polymorphic`` directory
|
||||||
you can import it, like your app directory (where your models.py and
|
into your project dir. If you want to use the management command
|
||||||
views.py files live).
|
``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
|
||||||
|
add-on module, copied to somewhere where it can be imported (like your
|
||||||
|
own app dir).
|
||||||
|
|
||||||
|
|
||||||
Defining Polymorphic Models
|
Defining Polymorphic Models
|
||||||
|
|
@ -41,7 +51,7 @@ 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.models import PolymorphicModel
|
||||||
|
|
||||||
class ModelA(PolymorphicModel):
|
class ModelA(PolymorphicModel):
|
||||||
field1 = models.CharField(max_length=10)
|
field1 = models.CharField(max_length=10)
|
||||||
|
|
@ -177,6 +187,15 @@ Non-Polymorphic Queries
|
||||||
Django manager. Of course, arbitrary custom managers may be
|
Django manager. Of course, arbitrary custom managers may be
|
||||||
added to the models as well.
|
added to the models as well.
|
||||||
|
|
||||||
|
manage.py dumpdata
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Django's standard ``dumpdata`` requires non-polymorphic
|
||||||
|
behaviour from the querysets it uses and produces incomplete
|
||||||
|
results with polymorphic models. Django_polymorphic includes
|
||||||
|
a slightly modified version, named ``polymorphic_dumpdata``.
|
||||||
|
Just use this command instead of Django's (see "installation/testing").
|
||||||
|
|
||||||
|
|
||||||
Custom Managers, Querysets & Inheritance
|
Custom Managers, Querysets & Inheritance
|
||||||
========================================
|
========================================
|
||||||
|
|
@ -205,23 +224,21 @@ the plain ``PolymorphicManager`` here.
|
||||||
Manager Inheritance / Propagation
|
Manager Inheritance / Propagation
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
Polymorphic models unconditionally inherit all managers from their
|
Polymorphic models unconditionally propagate (or inherit) all managers from
|
||||||
base models (as long as these are polymorphic).
|
their base models, as long as these are polymorphic. This means that all
|
||||||
|
managers inherited from polymorphic base models work just the same as if
|
||||||
|
they were defined in the new model.
|
||||||
|
|
||||||
An example (inheriting from MyModel above)::
|
An example (inheriting from MyModel above)::
|
||||||
|
|
||||||
class MyModel2(MyModel):
|
class MyModel2(MyModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Managers inherited from MyModel, delivering MyModel2 (and subclass) objects
|
# Managers inherited from MyModel:
|
||||||
|
# the regular 'objects' manager and the custom 'ordered_objects' manager
|
||||||
>>> MyModel2.objects.all()
|
>>> MyModel2.objects.all()
|
||||||
>>> MyModel2.ordered_objects.all()
|
>>> MyModel2.ordered_objects.all()
|
||||||
|
|
||||||
Perhaps a more correct way to describe this: With polymorphic models the
|
|
||||||
managers are always fully propagated from all polymorphic base models
|
|
||||||
(as strictly speaking all managers are always inherited with Django models).
|
|
||||||
|
|
||||||
|
|
||||||
Using a Custom Queryset Class
|
Using a Custom Queryset Class
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|
@ -377,6 +394,10 @@ 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 stability of the ``ContentType`` ids when combined with Django's
|
||||||
|
serialisation / fixtures has not yet been sufficiently
|
||||||
|
investigated (please see issue 4 on github).
|
||||||
|
|
||||||
+ 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
|
||||||
transferred twice from the database (an artefact of the current
|
transferred twice from the database (an artefact of the current
|
||||||
|
|
@ -393,13 +414,13 @@ Restrictions & Caveats
|
||||||
In General
|
In General
|
||||||
----------
|
----------
|
||||||
|
|
||||||
It is important to consider that this code is still very new and experimental.
|
It's important to consider that this code is still very new and experimental.
|
||||||
|
|
||||||
It has, however, been integrated into one larger system where all seems to work flawlessly
|
Right now it's suitable only for the more enterprising early adopters.
|
||||||
so far. A small number of people tested this code for their purposes and reported that it
|
|
||||||
works well for them.
|
|
||||||
|
|
||||||
Right now this module is suitable only for the more enterprising early adopters.
|
It does seem to work well for a number of people (including me), but
|
||||||
|
it's still very early and API changes, code reorganisations or further
|
||||||
|
schema changes are still a possibility.
|
||||||
|
|
||||||
|
|
||||||
Links
|
Links
|
||||||
|
|
|
||||||
68
README.rst
68
README.rst
|
|
@ -8,15 +8,15 @@
|
||||||
Usage, Examples, Installation & Documentation, Links
|
Usage, Examples, Installation & Documentation, Links
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
* Documentation_ and Overview_
|
* Please see the `Documentation and Examples`_ (or the short `Overview`_)
|
||||||
* `Discussion, Questions, Suggestions`_
|
* If you have comments or suggestions: `Discussion, Comments, Suggestions`_
|
||||||
* GitHub_ - Bitbucket_ - `Download as TGZ`_ or ZIP_
|
* The code can be found on GitHub_ and Bitbucket_, or downloaded as TGZ_ or ZIP_
|
||||||
|
|
||||||
.. _Documentation: http://bserve.webhop.org/wiki/django_polymorphic/doc
|
.. _Documentation and Examples: http://bserve.webhop.org/wiki/django_polymorphic/doc
|
||||||
.. _Discussion, Questions, Suggestions: http://django-polymorphic.blogspot.com/2010/01/messages.html
|
.. _Discussion, Questions, Suggestions: http://django-polymorphic.blogspot.com/2010/01/messages.html
|
||||||
.. _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
|
||||||
.. _Download as TGZ: http://github.com/bconstantin/django_polymorphic/tarball/master
|
.. _TGZ: http://github.com/bconstantin/django_polymorphic/tarball/master
|
||||||
.. _ZIP: http://github.com/bconstantin/django_polymorphic/zipball/master
|
.. _ZIP: http://github.com/bconstantin/django_polymorphic/zipball/master
|
||||||
.. _Overview: http://bserve.webhop.org/wiki/django_polymorphic
|
.. _Overview: http://bserve.webhop.org/wiki/django_polymorphic
|
||||||
|
|
||||||
|
|
@ -24,32 +24,60 @@ Usage, Examples, Installation & Documentation, Links
|
||||||
What is django_polymorphic good for?
|
What is django_polymorphic good for?
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
If ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``:
|
It causes objects being retrieved from the database to always be returned back
|
||||||
|
|
||||||
>>> Project.objects.all()
|
|
||||||
.
|
|
||||||
[ <Project: id 1, topic: "John's Gathering">,
|
|
||||||
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
|
||||||
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
|
||||||
|
|
||||||
In general, objects retrieved from the database are always returned back
|
|
||||||
with the same type/class and fields they were created and saved with.
|
with the same type/class and fields they were created and saved with.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
If ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``,
|
||||||
|
and we have saved one of each into the database::
|
||||||
|
|
||||||
|
>>> Project.objects.all()
|
||||||
|
.
|
||||||
|
[ <Project: id 1, topic: "John's Gathering">,
|
||||||
|
<ArtProject: id 2, topic: "Sculpting with Tim", artist: "T. Turner">,
|
||||||
|
<ResearchProject: id 3, topic: "Swallow Aerodynamics", supervisor: "Dr. Winter"> ]
|
||||||
|
|
||||||
It doesn't matter how these objects are retrieved: be it through the
|
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 this behaviour.
|
||||||
|
|
||||||
The resulting querysets are polymorphic, i.e they 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``,
|
|
||||||
that adds this functionality to Django's model inheritance system
|
|
||||||
(for models that request this behaviour).
|
|
||||||
|
|
||||||
|
|
||||||
Status
|
Status
|
||||||
------
|
------
|
||||||
|
|
||||||
This module is still very experimental. Please see the docs for current restrictions,
|
It's important to consider that this code is still very new and
|
||||||
caveats, and performance implications.
|
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 well for a number of people (including me), but
|
||||||
|
it's still very early and API changes, code reorganisations or further
|
||||||
|
schema changes are still a possibility.
|
||||||
|
|
||||||
|
|
||||||
|
News
|
||||||
|
----
|
||||||
|
|
||||||
|
**2010-1-29:**
|
||||||
|
|
||||||
|
Restructured django_polymorphic into a regular Django add-on
|
||||||
|
application. This is needed for the management commands, and
|
||||||
|
also seems to be a generally good idea for future enhancements
|
||||||
|
as well (and it makes sure the tests are always included).
|
||||||
|
|
||||||
|
The ``poly`` app - until now being used for test purposes only
|
||||||
|
- has been renamed to ``polymorphic``. See DOCS.rst
|
||||||
|
("installation/testing") for more info.
|
||||||
|
|
||||||
|
**2010-1-26:**
|
||||||
|
|
||||||
|
IMPORTANT - database schema change (more info in change log).
|
||||||
|
I hope I got this change in early enough before anyone started to use
|
||||||
|
polymorphic.py in earnest. Sorry for any inconvenience.
|
||||||
|
This should be the final DB schema now.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rm /var/tmp/django-polymorphic-test-db.sqlite3
|
||||||
|
./manage.py syncdb
|
||||||
|
|
||||||
|
|
@ -5,10 +5,11 @@ This module is a scratchpad for general development, testing & debugging
|
||||||
|
|
||||||
from django.core.management.base import NoArgsCommand
|
from django.core.management.base import NoArgsCommand
|
||||||
from django.db.models import connection
|
from django.db.models import connection
|
||||||
from poly.models import *
|
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import settings
|
import settings
|
||||||
|
|
||||||
|
from pexp.models import *
|
||||||
|
|
||||||
def reset_queries():
|
def reset_queries():
|
||||||
connection.queries=[]
|
connection.queries=[]
|
||||||
|
|
||||||
|
|
@ -22,19 +23,19 @@ class Command(NoArgsCommand):
|
||||||
print 'polycmd - sqlite test db is stored in:',settings.DATABASE_NAME
|
print 'polycmd - sqlite test db is stored in:',settings.DATABASE_NAME
|
||||||
print
|
print
|
||||||
|
|
||||||
Project.objects.all().delete()
|
|
||||||
o=Project.objects.create(topic="John's gathering")
|
|
||||||
o=ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
|
|
||||||
o=ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
|
||||||
|
|
||||||
print Project.objects.all()
|
|
||||||
print
|
|
||||||
|
|
||||||
ModelA.objects.all().delete()
|
ModelA.objects.all().delete()
|
||||||
o=ModelA.objects.create(field1='A1')
|
o=ModelA.objects.create(field1='A1')
|
||||||
o=ModelB.objects.create(field1='B1', field2='B2')
|
o=ModelB.objects.create(field1='B1', field2='B2')
|
||||||
o=ModelC.objects.create(field1='C1', field2='C2', field3='C3')
|
o=ModelC.objects.create(field1='C1', field2='C2', field3='C3')
|
||||||
|
|
||||||
print ModelA.objects.all()
|
print ModelA.objects.all()
|
||||||
|
print
|
||||||
|
|
||||||
|
Project.objects.all().delete()
|
||||||
|
o=Project.objects.create(topic="John's gathering")
|
||||||
|
o=ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner")
|
||||||
|
o=ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
|
||||||
|
print Project.objects.all()
|
||||||
|
print
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from polymorphic.models import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes
|
||||||
|
|
||||||
|
|
||||||
|
class Project(ShowFields, 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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
"""
|
||||||
|
This file demonstrates two different styles of tests (one doctest and one
|
||||||
|
unittest). These will both pass when you run "manage.py test".
|
||||||
|
|
||||||
|
Replace these with more appropriate tests for your application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
class SimpleTest(TestCase):
|
||||||
|
def test_basic_addition(self):
|
||||||
|
"""
|
||||||
|
Tests that 1 + 1 always equals 2.
|
||||||
|
"""
|
||||||
|
self.failUnlessEqual(1 + 1, 2)
|
||||||
|
|
||||||
|
__test__ = {"doctest": """
|
||||||
|
Another way to test that 1 + 1 is equal to 2.
|
||||||
|
|
||||||
|
>>> 1 + 1 == 2
|
||||||
|
True
|
||||||
|
"""}
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# Create your views here.
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
|
|
||||||
import django
|
|
||||||
|
|
||||||
if django.VERSION[:2]==(1,1):
|
|
||||||
from polymorphic_dumpdata_11 import Command
|
|
||||||
|
|
||||||
elif django.VERSION[:2]==(1,2):
|
|
||||||
from polymorphic_dumpdata_12 import Command
|
|
||||||
|
|
||||||
else:
|
|
||||||
assert False, 'Django version not supported'
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes
|
|
||||||
|
|
||||||
class PlainA(models.Model):
|
|
||||||
field1 = models.CharField(max_length=10)
|
|
||||||
class PlainB(PlainA):
|
|
||||||
field2 = models.CharField(max_length=10)
|
|
||||||
class PlainC(PlainB):
|
|
||||||
field3 = models.CharField(max_length=10)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
class Base(PolymorphicModel):
|
|
||||||
field_b = models.CharField(max_length=10)
|
|
||||||
class ModelX(Base):
|
|
||||||
field_x = models.CharField(max_length=10)
|
|
||||||
class ModelY(Base):
|
|
||||||
field_y = models.CharField(max_length=10)
|
|
||||||
|
|
||||||
class Enhance_Plain(models.Model):
|
|
||||||
field_p = models.CharField(max_length=10)
|
|
||||||
class Enhance_Base(ShowFieldsAndTypes, PolymorphicModel):
|
|
||||||
field_b = models.CharField(max_length=10)
|
|
||||||
class Enhance_Inherit(Enhance_Base, Enhance_Plain):
|
|
||||||
field_i = models.CharField(max_length=10)
|
|
||||||
|
|
||||||
|
|
||||||
class DiamondBase(models.Model):
|
|
||||||
field_b = models.CharField(max_length=10)
|
|
||||||
class DiamondX(DiamondBase):
|
|
||||||
field_x = models.CharField(max_length=10)
|
|
||||||
class DiamondY(DiamondBase):
|
|
||||||
field_y = models.CharField(max_length=10)
|
|
||||||
class DiamondXY(DiamondX, DiamondY):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class RelationBase(ShowFieldsAndTypes, PolymorphicModel):
|
|
||||||
field_base = models.CharField(max_length=10)
|
|
||||||
fk = models.ForeignKey('self', null=True)
|
|
||||||
m2m = models.ManyToManyField('self')
|
|
||||||
class RelationA(RelationBase):
|
|
||||||
field_a = models.CharField(max_length=10)
|
|
||||||
class RelationB(RelationBase):
|
|
||||||
field_b = models.CharField(max_length=10)
|
|
||||||
class RelationBC(RelationB):
|
|
||||||
field_c = models.CharField(max_length=10)
|
|
||||||
|
|
||||||
class RelatingModel(models.Model):
|
|
||||||
many2many = models.ManyToManyField(ModelA)
|
|
||||||
|
|
||||||
class MyManager(PolymorphicManager):
|
|
||||||
def get_query_set(self):
|
|
||||||
return super(MyManager, self).get_query_set().order_by('-field1')
|
|
||||||
class ModelWithMyManager(ShowFieldsAndTypes, ModelA):
|
|
||||||
objects = MyManager()
|
|
||||||
field4 = models.CharField(max_length=10)
|
|
||||||
|
|
||||||
class MROBase1(PolymorphicModel):
|
|
||||||
objects = MyManager()
|
|
||||||
field1 = models.CharField(max_length=10) # needed as MyManager uses it
|
|
||||||
class MROBase2(MROBase1):
|
|
||||||
pass # Django vanilla inheritance does not inherit MyManager as _default_manager here
|
|
||||||
class MROBase3(models.Model):
|
|
||||||
objects = PolymorphicManager()
|
|
||||||
class MRODerived(MROBase2, MROBase3):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MgrInheritA(models.Model):
|
|
||||||
mgrA = models.Manager()
|
|
||||||
mgrA2 = models.Manager()
|
|
||||||
field1 = models.CharField(max_length=10)
|
|
||||||
class MgrInheritB(MgrInheritA):
|
|
||||||
mgrB = models.Manager()
|
|
||||||
field2 = models.CharField(max_length=10)
|
|
||||||
class MgrInheritC(ShowFieldsAndTypes, MgrInheritB):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Project(ShowFields,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)
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""
|
||||||
|
polymorphic_dumpdata is just a slightly modified version
|
||||||
|
of Django's dumpdata. In the long term, patching Django's
|
||||||
|
dumpdata definitely is a better solution.
|
||||||
|
|
||||||
|
Use the Django 1.1 or 1.2 variant of dumpdata, depending of the
|
||||||
|
Django version used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import django
|
||||||
|
|
||||||
|
if django.VERSION[:2]==(1,1):
|
||||||
|
from polymorphic_dumpdata_11 import Command
|
||||||
|
|
||||||
|
elif django.VERSION[:2]==(1,2):
|
||||||
|
from polymorphic_dumpdata_12 import Command
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert False, 'Django version not supported'
|
||||||
|
|
@ -75,6 +75,8 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
for model in model_list:
|
for model in model_list:
|
||||||
if not model._meta.proxy:
|
if not model._meta.proxy:
|
||||||
|
|
||||||
|
#### patch for django_polymorphic ######################################################
|
||||||
# modified for django_polymorphic compatibility:
|
# modified for django_polymorphic compatibility:
|
||||||
# do not use polymorphic queryset for serialisation
|
# do not use polymorphic queryset for serialisation
|
||||||
# (as the dumpdata/serializer implementation depends
|
# (as the dumpdata/serializer implementation depends
|
||||||
|
|
@ -80,6 +80,8 @@ class Command(BaseCommand):
|
||||||
objects = []
|
objects = []
|
||||||
for model in sort_dependencies(app_list.items()):
|
for model in sort_dependencies(app_list.items()):
|
||||||
if not model._meta.proxy:
|
if not model._meta.proxy:
|
||||||
|
|
||||||
|
#### patch for django_polymorphic ######################################################
|
||||||
# modified for django_polymorphic compatibility:
|
# modified for django_polymorphic compatibility:
|
||||||
# do not use polymorphic queryset for serialisation
|
# do not use polymorphic queryset for serialisation
|
||||||
# (as the dumpdata/serializer implementation depends
|
# (as the dumpdata/serializer implementation depends
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes
|
||||||
|
|
@ -321,7 +321,7 @@ def _translate_polymorphic_field_path(queryset_model, field_path):
|
||||||
submodels = {}
|
submodels = {}
|
||||||
add_all_sub_models(queryset_model, submodels)
|
add_all_sub_models(queryset_model, submodels)
|
||||||
model = submodels.get(classname, None)
|
model = submodels.get(classname, None)
|
||||||
assert model, 'PolymorphicModel: model %s not found (not a subclass of %s)!' % (model.__name__, queryset_model.__name__)
|
assert model, 'PolymorphicModel: model %s not found (not a subclass of %s)!' % (classname, queryset_model.__name__)
|
||||||
|
|
||||||
# create new field path for expressions, e.g. for baseclass=ModelA, myclass=ModelC
|
# create new field path for expressions, e.g. for baseclass=ModelA, myclass=ModelC
|
||||||
# 'modelb__modelc" is returned
|
# 'modelb__modelc" is returned
|
||||||
|
|
@ -1,6 +1,104 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#disabletests = 1
|
|
||||||
"""
|
import settings
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from models import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes
|
||||||
|
|
||||||
|
class PlainA(models.Model):
|
||||||
|
field1 = models.CharField(max_length=10)
|
||||||
|
class PlainB(PlainA):
|
||||||
|
field2 = models.CharField(max_length=10)
|
||||||
|
class PlainC(PlainB):
|
||||||
|
field3 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class Model2A(PolymorphicModel):
|
||||||
|
field1 = models.CharField(max_length=10)
|
||||||
|
class Model2B(Model2A):
|
||||||
|
field2 = models.CharField(max_length=10)
|
||||||
|
class Model2C(Model2B):
|
||||||
|
field3 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class Base(PolymorphicModel):
|
||||||
|
field_b = models.CharField(max_length=10)
|
||||||
|
class ModelX(Base):
|
||||||
|
field_x = models.CharField(max_length=10)
|
||||||
|
class ModelY(Base):
|
||||||
|
field_y = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class Enhance_Plain(models.Model):
|
||||||
|
field_p = models.CharField(max_length=10)
|
||||||
|
class Enhance_Base(ShowFieldsAndTypes, PolymorphicModel):
|
||||||
|
field_b = models.CharField(max_length=10)
|
||||||
|
class Enhance_Inherit(Enhance_Base, Enhance_Plain):
|
||||||
|
field_i = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
|
||||||
|
class DiamondBase(models.Model):
|
||||||
|
field_b = models.CharField(max_length=10)
|
||||||
|
class DiamondX(DiamondBase):
|
||||||
|
field_x = models.CharField(max_length=10)
|
||||||
|
class DiamondY(DiamondBase):
|
||||||
|
field_y = models.CharField(max_length=10)
|
||||||
|
class DiamondXY(DiamondX, DiamondY):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class RelationBase(ShowFieldsAndTypes, PolymorphicModel):
|
||||||
|
field_base = models.CharField(max_length=10)
|
||||||
|
fk = models.ForeignKey('self', null=True)
|
||||||
|
m2m = models.ManyToManyField('self')
|
||||||
|
class RelationA(RelationBase):
|
||||||
|
field_a = models.CharField(max_length=10)
|
||||||
|
class RelationB(RelationBase):
|
||||||
|
field_b = models.CharField(max_length=10)
|
||||||
|
class RelationBC(RelationB):
|
||||||
|
field_c = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class RelatingModel(models.Model):
|
||||||
|
many2many = models.ManyToManyField(Model2A)
|
||||||
|
|
||||||
|
class MyManager(PolymorphicManager):
|
||||||
|
def get_query_set(self):
|
||||||
|
return super(MyManager, self).get_query_set().order_by('-field1')
|
||||||
|
class ModelWithMyManager(ShowFieldsAndTypes, Model2A):
|
||||||
|
objects = MyManager()
|
||||||
|
field4 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
class MROBase1(PolymorphicModel):
|
||||||
|
objects = MyManager()
|
||||||
|
field1 = models.CharField(max_length=10) # needed as MyManager uses it
|
||||||
|
class MROBase2(MROBase1):
|
||||||
|
pass # Django vanilla inheritance does not inherit MyManager as _default_manager here
|
||||||
|
class MROBase3(models.Model):
|
||||||
|
objects = PolymorphicManager()
|
||||||
|
class MRODerived(MROBase2, MROBase3):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MgrInheritA(models.Model):
|
||||||
|
mgrA = models.Manager()
|
||||||
|
mgrA2 = models.Manager()
|
||||||
|
field1 = models.CharField(max_length=10)
|
||||||
|
class MgrInheritB(MgrInheritA):
|
||||||
|
mgrB = models.Manager()
|
||||||
|
field2 = models.CharField(max_length=10)
|
||||||
|
class MgrInheritC(ShowFieldsAndTypes, MgrInheritB):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class testclass(TestCase):
|
||||||
|
def test_diamond_inheritance(self):
|
||||||
|
# Django diamond problem
|
||||||
|
o = DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')
|
||||||
|
print 'DiamondXY fields 1: field_b "%s", field_x "%s", field_y "%s"' % (o.field_b, o.field_x, o.field_y)
|
||||||
|
o = DiamondXY.objects.get()
|
||||||
|
print 'DiamondXY fields 2: field_b "%s", field_x "%s", field_y "%s"' % (o.field_b, o.field_x, o.field_y)
|
||||||
|
if o.field_b != 'b': print '# Django model inheritance diamond problem detected'
|
||||||
|
|
||||||
|
__test__ = {"doctest": """
|
||||||
#######################################################
|
#######################################################
|
||||||
### Tests
|
### Tests
|
||||||
|
|
||||||
|
|
@ -8,45 +106,45 @@
|
||||||
|
|
||||||
### simple inheritance
|
### simple inheritance
|
||||||
|
|
||||||
>>> o=ModelA.objects.create(field1='A1')
|
>>> o=Model2A.objects.create(field1='A1')
|
||||||
>>> o=ModelB.objects.create(field1='B1', field2='B2')
|
>>> o=Model2B.objects.create(field1='B1', field2='B2')
|
||||||
>>> o=ModelC.objects.create(field1='C1', field2='C2', field3='C3')
|
>>> o=Model2C.objects.create(field1='C1', field2='C2', field3='C3')
|
||||||
|
|
||||||
>>> ModelA.objects.all()
|
>>> Model2A.objects.all()
|
||||||
[ <ModelA: id 1, field1 (CharField)>,
|
[ <Model2A: id 1, field1 (CharField)>,
|
||||||
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
<Model2B: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
# manual get_real_instance()
|
# manual get_real_instance()
|
||||||
>>> o=ModelA.base_objects.get(field1='C1')
|
>>> o=Model2A.base_objects.get(field1='C1')
|
||||||
>>> o.get_real_instance()
|
>>> o.get_real_instance()
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
|
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
|
||||||
|
|
||||||
### class filtering, instance_of, not_instance_of
|
### class filtering, instance_of, not_instance_of
|
||||||
|
|
||||||
>>> ModelA.objects.instance_of(ModelB)
|
>>> Model2A.objects.instance_of(Model2B)
|
||||||
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
>>> ModelA.objects.not_instance_of(ModelB)
|
>>> Model2A.objects.not_instance_of(Model2B)
|
||||||
[ <ModelA: id 1, field1 (CharField)> ]
|
[ <Model2A: id 1, field1 (CharField)> ]
|
||||||
|
|
||||||
### polymorphic filtering
|
### polymorphic filtering
|
||||||
|
|
||||||
>>> ModelA.objects.filter( Q( ModelB___field2 = 'B2' ) | Q( ModelC___field3 = 'C3' ) )
|
>>> Model2A.objects.filter( Q( Model2B___field2 = 'B2' ) | Q( Model2C___field3 = 'C3' ) )
|
||||||
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
|
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
### get & delete
|
### get & delete
|
||||||
|
|
||||||
>>> oa=ModelA.objects.get(id=2)
|
>>> oa=Model2A.objects.get(id=2)
|
||||||
>>> oa
|
>>> oa
|
||||||
<ModelB: id 2, field1 (CharField), field2 (CharField)>
|
<Model2B: id 2, field1 (CharField), field2 (CharField)>
|
||||||
|
|
||||||
>>> oa.delete()
|
>>> oa.delete()
|
||||||
>>> ModelA.objects.all()
|
>>> Model2A.objects.all()
|
||||||
[ <ModelA: id 1, field1 (CharField)>,
|
[ <Model2A: id 1, field1 (CharField)>,
|
||||||
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
|
||||||
|
|
||||||
### queryset combining
|
### queryset combining
|
||||||
|
|
||||||
|
|
@ -107,22 +205,22 @@
|
||||||
<ModelWithMyManager: id 4, field1 (CharField): "D1a", field4 (CharField): "D4a"> ]
|
<ModelWithMyManager: id 4, field1 (CharField): "D1a", field4 (CharField): "D4a"> ]
|
||||||
|
|
||||||
>>> type(ModelWithMyManager.objects)
|
>>> type(ModelWithMyManager.objects)
|
||||||
<class 'poly.models.MyManager'>
|
<class 'polymorphic.tests.MyManager'>
|
||||||
>>> type(ModelWithMyManager._default_manager)
|
>>> type(ModelWithMyManager._default_manager)
|
||||||
<class 'poly.models.MyManager'>
|
<class 'polymorphic.tests.MyManager'>
|
||||||
|
|
||||||
### Manager Inheritance
|
### Manager Inheritance
|
||||||
|
|
||||||
>>> type(MRODerived.objects) # MRO
|
>>> type(MRODerived.objects) # MRO
|
||||||
<class 'poly.models.MyManager'>
|
<class 'polymorphic.tests.MyManager'>
|
||||||
|
|
||||||
# check for correct default manager
|
# check for correct default manager
|
||||||
>>> type(MROBase1._default_manager)
|
>>> type(MROBase1._default_manager)
|
||||||
<class 'poly.models.MyManager'>
|
<class 'polymorphic.tests.MyManager'>
|
||||||
|
|
||||||
# Django vanilla inheritance does not inherit MyManager as _default_manager here
|
# Django vanilla inheritance does not inherit MyManager as _default_manager here
|
||||||
>>> type(MROBase2._default_manager)
|
>>> type(MROBase2._default_manager)
|
||||||
<class 'poly.models.MyManager'>
|
<class 'polymorphic.tests.MyManager'>
|
||||||
|
|
||||||
### Django model inheritance diamond problem, fails for Django 1.1
|
### Django model inheritance diamond problem, fails for Django 1.1
|
||||||
|
|
||||||
|
|
@ -132,22 +230,5 @@
|
||||||
|
|
||||||
>>> settings.DEBUG=False
|
>>> settings.DEBUG=False
|
||||||
|
|
||||||
"""
|
"""}
|
||||||
|
|
||||||
import settings
|
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from models import *
|
|
||||||
|
|
||||||
class testclass(TestCase):
|
|
||||||
def test_diamond_inheritance(self):
|
|
||||||
# Django diamond problem
|
|
||||||
o = DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')
|
|
||||||
print 'DiamondXY fields 1: field_b "%s", field_x "%s", field_y "%s"' % (o.field_b, o.field_x, o.field_y)
|
|
||||||
o = DiamondXY.objects.get()
|
|
||||||
print 'DiamondXY fields 2: field_b "%s", field_x "%s", field_y "%s"' % (o.field_b, o.field_x, o.field_y)
|
|
||||||
if o.field_b != 'b': print '# Django model inheritance diamond problem detected'
|
|
||||||
|
|
||||||
|
|
@ -77,5 +77,6 @@ INSTALLED_APPS = (
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
#'django.contrib.sessions',
|
#'django.contrib.sessions',
|
||||||
#'django.contrib.sites',
|
#'django.contrib.sites',
|
||||||
'poly', # this Django app is for testing and experimentation; not needed otherwise
|
'polymorphic', # only needed if you want to use polymorphic_dumpdata
|
||||||
|
'pexp', # this Django app is for testing and experimentation; not needed otherwise
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue