From b2357592cb15aac2c8884c71f3d3ca56912d75e0 Mon Sep 17 00:00:00 2001 From: Bert Constantin Date: Fri, 22 Oct 2010 12:11:34 +0200 Subject: [PATCH] make sure 'base_manager is not inherited (but managed by Django instead). (This seems more correct but it doen't seem to make any difference.) Also added related test cases. --- README.html | 1 - README.rst | 1 - polymorphic/base.py | 4 +- polymorphic/polymorphic_model.py | 9 ++-- polymorphic/tests.py | 82 +++++++++++++++++++++++++++----- 5 files changed, 77 insertions(+), 20 deletions(-) diff --git a/README.html b/README.html index 08c4d95..32c5ccc 100644 --- a/README.html +++ b/README.html @@ -213,7 +213,6 @@ ul.auto-toc {

Polymorphic Models for Django

-

.

Quick Start, Docs, Contributing

    diff --git a/README.rst b/README.rst index 997566e..c017afa 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,6 @@ Polymorphic Models for Django ============================= -. Quick Start, Docs, Contributing ------------------------------- diff --git a/polymorphic/base.py b/polymorphic/base.py index 7210558..7bd0f3d 100644 --- a/polymorphic/base.py +++ b/polymorphic/base.py @@ -92,9 +92,11 @@ class PolymorphicModelBase(ModelBase): for key, manager in base.__dict__.items(): if type(manager) == models.manager.ManagerDescriptor: manager = manager.manager if not isinstance(manager, models.Manager): continue + if key in ['_base_manager']: continue # let Django handle _base_manager if key in attrs: continue if key in add_managers_keys: continue # manager with that name already added, skip - if manager._inherited: continue # inherited managers have no significance, they are just copies + if manager._inherited: continue # inherited managers (on the bases) have no significance, they are just copies + #print >>sys.stderr,'##',self.__name__, key if isinstance(manager, PolymorphicManager): # validate any inherited polymorphic managers self.validate_model_manager(manager, self.__name__, key) add_managers.append((base.__name__, key, manager)) diff --git a/polymorphic/polymorphic_model.py b/polymorphic/polymorphic_model.py index b39cbec..29c7e3e 100644 --- a/polymorphic/polymorphic_model.py +++ b/polymorphic/polymorphic_model.py @@ -135,25 +135,24 @@ class PolymorphicModel(models.Model): our appropriate base_objects manager. """ super(PolymorphicModel, self).__init__(*args, ** kwargs) + if self.__class__.polymorphic_super_sub_accessors_replaced: return self.__class__.polymorphic_super_sub_accessors_replaced = True - def create_accessor_function_for_model(model): + def create_accessor_function_for_model(model, accessor_name): def accessor_function(self): attr = model.base_objects.get(pk=self.pk) return attr return accessor_function subclasses_and_superclasses_accessors = self.get_inheritance_relation_fields_and_models() - #print '###',self.__class__.__name__,subclasses_and_superclasses_accessors from django.db.models.fields.related import SingleRelatedObjectDescriptor, ReverseSingleRelatedObjectDescriptor - for name,model in subclasses_and_superclasses_accessors.iteritems(): orig_accessor = getattr(self.__class__, name, None) if type(orig_accessor) in [SingleRelatedObjectDescriptor,ReverseSingleRelatedObjectDescriptor]: - #print 'replacing',name, orig_accessor - setattr(self.__class__, name, property(create_accessor_function_for_model(model)) ) + #print >>sys.stderr, '---------- replacing',name, orig_accessor + setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)) ) def get_inheritance_relation_fields_and_models(self): """helper function for __init__: diff --git a/polymorphic/tests.py b/polymorphic/tests.py index 8c0721b..bce835c 100644 --- a/polymorphic/tests.py +++ b/polymorphic/tests.py @@ -4,6 +4,7 @@ """ import settings +import sys from django.test import TestCase from django.db.models.query import QuerySet @@ -29,6 +30,8 @@ class Model2B(Model2A): field2 = models.CharField(max_length=10) class Model2C(Model2B): field3 = models.CharField(max_length=10) +class Model2D(Model2C): + field4 = models.CharField(max_length=10) class ModelShow1(ShowFieldType,PolymorphicModel): field1 = models.CharField(max_length=10) @@ -84,6 +87,13 @@ class RelationBC(RelationB): class RelatingModel(models.Model): many2many = models.ManyToManyField(Model2A) +class One2OneRelatingModel(PolymorphicModel): + one2one = models.OneToOneField(Model2A) + field1 = models.CharField(max_length=10) + +class One2OneRelatingModelDerived(One2OneRelatingModel): + field2 = models.CharField(max_length=10) + class MyManager(PolymorphicManager): def get_query_set(self): return super(MyManager, self).get_query_set().order_by('-field1') @@ -242,6 +252,9 @@ class testclass(TestCase): entry1 = BlogEntry_limit_choices_to.objects.create(blog=blog_b, text='bla2') entry2 = BlogEntry_limit_choices_to.objects.create(blog=blog_b, text='bla2') + +def show_base_manager(model): + print type(model._base_manager),model._base_manager.model __test__ = {"doctest": """ ####################################################### @@ -249,19 +262,18 @@ __test__ = {"doctest": """ >>> settings.DEBUG=True -#>>> get_version() -#'1.0 rc1' - ### simple inheritance >>> o=Model2A.objects.create(field1='A1') >>> o=Model2B.objects.create(field1='B1', field2='B2') >>> o=Model2C.objects.create(field1='C1', field2='C2', field3='C3') - - [ , +>>> o=Model2D.objects.create(field1='D1', field2='D2', field3='D3', field4='D4') +>>> Model2A.objects.all() +[ , , - ] + , + ] # manual get_real_instance() >>> o=Model2A.base_objects.get(field1='C1') @@ -269,6 +281,48 @@ __test__ = {"doctest": """ +### test inheritance pointers & _base_managers + +>>> show_base_manager(PlainA) + +>>> show_base_manager(PlainB) + +>>> show_base_manager(PlainC) + +>>> show_base_manager(Model2A) + +>>> show_base_manager(Model2B) + +>>> show_base_manager(Model2C) + +>>> show_base_manager(One2OneRelatingModel) + +>>> show_base_manager(One2OneRelatingModelDerived) + + +>>> o=Model2A.base_objects.get(field1='C1') +>>> o.model2b + + +>>> o=Model2B.base_objects.get(field1='C1') +>>> o.model2c + + + +### OneToOneField, test both directions for polymorphism + +>>> a=Model2A.base_objects.get(field1='C1') +>>> b=One2OneRelatingModelDerived.objects.create(one2one=a, field1='f1', field2='f2') +>>> b.one2one # this result is basically wrong, probably due to Django cacheing (we used base_objects), but should not be a problem + +>>> c=One2OneRelatingModelDerived.objects.get(field1='f1') +>>> c.one2one + + +>>> a.one2onerelatingmodel + + + ### ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent, also with annotate() >>> o=ModelShow1.objects.create(field1='abc') @@ -315,15 +369,18 @@ __test__ = {"doctest": """ >>> Model2A.objects.instance_of(Model2B) [ , - ] + , + ] >>> Model2A.objects.filter(instance_of=Model2B) [ , - ] + , + ] >>> Model2A.objects.filter(Q(instance_of=Model2B)) [ , - ] + , + ] >>> Model2A.objects.not_instance_of(Model2B) [ ] @@ -345,7 +402,8 @@ __test__ = {"doctest": """ >>> oa.delete() >>> Model2A.objects.all() [ , - ] + , + ] ### queryset combining @@ -405,8 +463,8 @@ __test__ = {"doctest": """ >>> o=ModelWithMyManager.objects.create(field1='D1b', field4='D4b') >>> ModelWithMyManager.objects.all() -[ , - ] +[ , + ] >>> type(ModelWithMyManager.objects)