From 8bdda93e76133ced44327f89703c7305955a5119 Mon Sep 17 00:00:00 2001 From: Bert Constantin Date: Fri, 29 Jan 2010 00:41:54 +0100 Subject: [PATCH] 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. --- DOCS.rst | 65 ++++--- README.rst | 68 +++++-- dbreset | 5 + {poly => pexp}/__init__.py | 0 {poly => pexp}/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/pcmd.py | 21 ++- pexp/models.py | 18 ++ pexp/tests.py | 23 +++ pexp/views.py | 1 + .../commands/polymorphic_dumpdata.py | 11 -- poly/models.py | 90 --------- polymorphic/__init__.py | 0 polymorphic/management/__init__.py | 0 polymorphic/management/commands/__init__.py | 0 .../commands/polymorphic_dumpdata.py | 19 ++ .../commands/polymorphic_dumpdata_11.py | 2 + .../commands/polymorphic_dumpdata_12.py | 2 + polymorphic/models.py | 3 + {poly => polymorphic}/polymorphic.py | 2 +- {poly => polymorphic}/tests.py | 175 +++++++++++++----- settings.py | 3 +- 22 files changed, 306 insertions(+), 202 deletions(-) create mode 100755 dbreset rename {poly => pexp}/__init__.py (100%) rename {poly => pexp}/management/__init__.py (100%) rename {poly => pexp}/management/commands/__init__.py (100%) rename poly/management/commands/polycmd.py => pexp/management/commands/pcmd.py (96%) create mode 100644 pexp/models.py create mode 100644 pexp/tests.py create mode 100644 pexp/views.py delete mode 100644 poly/management/commands/polymorphic_dumpdata.py delete mode 100644 poly/models.py create mode 100644 polymorphic/__init__.py create mode 100644 polymorphic/management/__init__.py create mode 100644 polymorphic/management/commands/__init__.py create mode 100644 polymorphic/management/commands/polymorphic_dumpdata.py rename {poly => polymorphic}/management/commands/polymorphic_dumpdata_11.py (97%) rename {poly => polymorphic}/management/commands/polymorphic_dumpdata_12.py (98%) create mode 100644 polymorphic/models.py rename {poly => polymorphic}/polymorphic.py (99%) rename {poly => polymorphic}/tests.py (51%) diff --git a/DOCS.rst b/DOCS.rst index 93b168a..d57e4f4 100644 --- a/DOCS.rst +++ b/DOCS.rst @@ -8,7 +8,9 @@ Requirements ------------ 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 ------- @@ -18,20 +20,28 @@ that may be used for tests or experiments. To run the included test suite, execute:: - ./manage test poly + ./manage test polymorphic -'management/commands/polycmd.py' can be used for experiments -- modify this file to your liking, then run:: +The management command ``pcmd.py`` in the app ``pexp`` (Polymorphic EXPerimenting) +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 polycmd + ./manage pcmd Using polymorphic models in your own projects --------------------------------------------- -Copy polymorphic.py (from the 'poly' dir) into a directory from where -you can import it, like your app directory (where your models.py and -views.py files live). +The best way for now is probably to just copy the ``polymorphic`` directory +into your project dir. If you want to use the management command +``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 @@ -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 inheriting from your base class will be polymorphic as well:: - from polymorphic import PolymorphicModel + from polymorphic.models import PolymorphicModel class ModelA(PolymorphicModel): field1 = models.CharField(max_length=10) @@ -177,6 +187,15 @@ Non-Polymorphic Queries Django manager. Of course, arbitrary custom managers may be 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 ======================================== @@ -205,23 +224,21 @@ the plain ``PolymorphicManager`` here. Manager Inheritance / Propagation --------------------------------- -Polymorphic models unconditionally inherit all managers from their -base models (as long as these are polymorphic). +Polymorphic models unconditionally propagate (or inherit) all managers from +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):: class MyModel2(MyModel): 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.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 ----------------------------- @@ -377,6 +394,10 @@ Restrictions & Caveats ContentType table needs to be corrected too, if the db content 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 instances of a subclass, the base class fields are currently transferred twice from the database (an artefact of the current @@ -393,13 +414,13 @@ Restrictions & Caveats 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 -so far. A small number of people tested this code for their purposes and reported that it -works well for them. +Right now it's suitable only for the more enterprising early adopters. -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 diff --git a/README.rst b/README.rst index 55e98ba..b10c8bb 100644 --- a/README.rst +++ b/README.rst @@ -8,15 +8,15 @@ Usage, Examples, Installation & Documentation, Links ---------------------------------------------------- -* Documentation_ and Overview_ -* `Discussion, Questions, Suggestions`_ -* GitHub_ - Bitbucket_ - `Download as TGZ`_ or ZIP_ +* Please see the `Documentation and Examples`_ (or the short `Overview`_) +* If you have comments or suggestions: `Discussion, Comments, Suggestions`_ +* 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 .. _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 +.. _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 @@ -24,32 +24,60 @@ Usage, Examples, Installation & Documentation, Links What is django_polymorphic good for? ------------------------------------ -If ``ArtProject`` and ``ResearchProject`` inherit from the model ``Project``: - ->>> Project.objects.all() -. -[ , - , - ] - -In general, objects retrieved from the database are always returned back +It causes objects being retrieved from the database to always be returned back 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() + . + [ , + , + ] + It doesn't matter how these objects are retrieved: be it through the model's own managers/querysets, ForeignKeys, ManyToManyFields or OneToOneFields. +``django_polymorphic`` does this only for models that explicitly request this behaviour. + The resulting querysets are polymorphic, i.e they may deliver 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 ------ -This module is still very experimental. Please see the docs for current restrictions, -caveats, and performance implications. +It's important to consider that this code is still very new and +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. diff --git a/dbreset b/dbreset new file mode 100755 index 0000000..d7c3e26 --- /dev/null +++ b/dbreset @@ -0,0 +1,5 @@ +#!/bin/bash + +rm /var/tmp/django-polymorphic-test-db.sqlite3 +./manage.py syncdb + diff --git a/poly/__init__.py b/pexp/__init__.py similarity index 100% rename from poly/__init__.py rename to pexp/__init__.py diff --git a/poly/management/__init__.py b/pexp/management/__init__.py similarity index 100% rename from poly/management/__init__.py rename to pexp/management/__init__.py diff --git a/poly/management/commands/__init__.py b/pexp/management/commands/__init__.py similarity index 100% rename from poly/management/commands/__init__.py rename to pexp/management/commands/__init__.py diff --git a/poly/management/commands/polycmd.py b/pexp/management/commands/pcmd.py similarity index 96% rename from poly/management/commands/polycmd.py rename to pexp/management/commands/pcmd.py index 18ee8a0..cd0fd65 100644 --- a/poly/management/commands/polycmd.py +++ b/pexp/management/commands/pcmd.py @@ -5,10 +5,11 @@ This module is a scratchpad for general development, testing & debugging from django.core.management.base import NoArgsCommand from django.db.models import connection -from poly.models import * from pprint import pprint import settings +from pexp.models import * + def reset_queries(): connection.queries=[] @@ -22,19 +23,19 @@ class Command(NoArgsCommand): print 'polycmd - sqlite test db is stored in:',settings.DATABASE_NAME 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() o=ModelA.objects.create(field1='A1') o=ModelB.objects.create(field1='B1', field2='B2') o=ModelC.objects.create(field1='C1', field2='C2', field3='C3') - 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 + diff --git a/pexp/models.py b/pexp/models.py new file mode 100644 index 0000000..06f3a90 --- /dev/null +++ b/pexp/models.py @@ -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) diff --git a/pexp/tests.py b/pexp/tests.py new file mode 100644 index 0000000..2247054 --- /dev/null +++ b/pexp/tests.py @@ -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 +"""} + diff --git a/pexp/views.py b/pexp/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/pexp/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/poly/management/commands/polymorphic_dumpdata.py b/poly/management/commands/polymorphic_dumpdata.py deleted file mode 100644 index 4316530..0000000 --- a/poly/management/commands/polymorphic_dumpdata.py +++ /dev/null @@ -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' diff --git a/poly/models.py b/poly/models.py deleted file mode 100644 index c833644..0000000 --- a/poly/models.py +++ /dev/null @@ -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) - diff --git a/polymorphic/__init__.py b/polymorphic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/polymorphic/management/__init__.py b/polymorphic/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/polymorphic/management/commands/__init__.py b/polymorphic/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/polymorphic/management/commands/polymorphic_dumpdata.py b/polymorphic/management/commands/polymorphic_dumpdata.py new file mode 100644 index 0000000..ca7e452 --- /dev/null +++ b/polymorphic/management/commands/polymorphic_dumpdata.py @@ -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' diff --git a/poly/management/commands/polymorphic_dumpdata_11.py b/polymorphic/management/commands/polymorphic_dumpdata_11.py similarity index 97% rename from poly/management/commands/polymorphic_dumpdata_11.py rename to polymorphic/management/commands/polymorphic_dumpdata_11.py index 4d8897c..bc8f47d 100644 --- a/poly/management/commands/polymorphic_dumpdata_11.py +++ b/polymorphic/management/commands/polymorphic_dumpdata_11.py @@ -75,6 +75,8 @@ class Command(BaseCommand): for model in model_list: if not model._meta.proxy: + +#### patch for django_polymorphic ###################################################### # modified for django_polymorphic compatibility: # do not use polymorphic queryset for serialisation # (as the dumpdata/serializer implementation depends diff --git a/poly/management/commands/polymorphic_dumpdata_12.py b/polymorphic/management/commands/polymorphic_dumpdata_12.py similarity index 98% rename from poly/management/commands/polymorphic_dumpdata_12.py rename to polymorphic/management/commands/polymorphic_dumpdata_12.py index ad3a38f..03ab2fd 100644 --- a/poly/management/commands/polymorphic_dumpdata_12.py +++ b/polymorphic/management/commands/polymorphic_dumpdata_12.py @@ -80,6 +80,8 @@ class Command(BaseCommand): objects = [] for model in sort_dependencies(app_list.items()): if not model._meta.proxy: + +#### patch for django_polymorphic ###################################################### # modified for django_polymorphic compatibility: # do not use polymorphic queryset for serialisation # (as the dumpdata/serializer implementation depends diff --git a/polymorphic/models.py b/polymorphic/models.py new file mode 100644 index 0000000..564fbe2 --- /dev/null +++ b/polymorphic/models.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes diff --git a/poly/polymorphic.py b/polymorphic/polymorphic.py similarity index 99% rename from poly/polymorphic.py rename to polymorphic/polymorphic.py index 1ba0f7d..66c5816 100644 --- a/poly/polymorphic.py +++ b/polymorphic/polymorphic.py @@ -321,7 +321,7 @@ def _translate_polymorphic_field_path(queryset_model, field_path): submodels = {} add_all_sub_models(queryset_model, submodels) 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 # 'modelb__modelc" is returned diff --git a/poly/tests.py b/polymorphic/tests.py similarity index 51% rename from poly/tests.py rename to polymorphic/tests.py index c5bb6cc..a3c2457 100644 --- a/poly/tests.py +++ b/polymorphic/tests.py @@ -1,6 +1,104 @@ # -*- 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 @@ -8,45 +106,45 @@ ### simple inheritance ->>> o=ModelA.objects.create(field1='A1') ->>> o=ModelB.objects.create(field1='B1', field2='B2') ->>> o=ModelC.objects.create(field1='C1', field2='C2', field3='C3') +>>> o=Model2A.objects.create(field1='A1') +>>> o=Model2B.objects.create(field1='B1', field2='B2') +>>> o=Model2C.objects.create(field1='C1', field2='C2', field3='C3') ->>> ModelA.objects.all() -[ , - , - ] +>>> Model2A.objects.all() +[ , + , + ] # manual get_real_instance() ->>> o=ModelA.base_objects.get(field1='C1') +>>> o=Model2A.base_objects.get(field1='C1') >>> o.get_real_instance() - + ### class filtering, instance_of, not_instance_of ->>> ModelA.objects.instance_of(ModelB) -[ , - ] +>>> Model2A.objects.instance_of(Model2B) +[ , + ] ->>> ModelA.objects.not_instance_of(ModelB) -[ ] +>>> Model2A.objects.not_instance_of(Model2B) +[ ] ### polymorphic filtering ->>> ModelA.objects.filter( Q( ModelB___field2 = 'B2' ) | Q( ModelC___field3 = 'C3' ) ) -[ , - ] +>>> Model2A.objects.filter( Q( Model2B___field2 = 'B2' ) | Q( Model2C___field3 = 'C3' ) ) +[ , + ] ### get & delete ->>> oa=ModelA.objects.get(id=2) +>>> oa=Model2A.objects.get(id=2) >>> oa - + >>> oa.delete() ->>> ModelA.objects.all() -[ , - ] +>>> Model2A.objects.all() +[ , + ] ### queryset combining @@ -107,22 +205,22 @@ ] >>> type(ModelWithMyManager.objects) - + >>> type(ModelWithMyManager._default_manager) - + ### Manager Inheritance >>> type(MRODerived.objects) # MRO - + # check for correct default manager >>> type(MROBase1._default_manager) - + # Django vanilla inheritance does not inherit MyManager as _default_manager here >>> type(MROBase2._default_manager) - + ### Django model inheritance diamond problem, fails for Django 1.1 @@ -132,22 +230,5 @@ >>> 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' +"""} \ No newline at end of file diff --git a/settings.py b/settings.py index a3835f7..f646415 100644 --- a/settings.py +++ b/settings.py @@ -77,5 +77,6 @@ INSTALLED_APPS = ( 'django.contrib.contenttypes', #'django.contrib.sessions', #'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 )