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.
This commit is contained in:
Bert Constantin
2010-10-22 12:11:34 +02:00
parent 6628145af7
commit b2357592cb
5 changed files with 77 additions and 20 deletions
-1
View File
@@ -213,7 +213,6 @@ ul.auto-toc {
<div class="section" id="polymorphic-models-for-django"> <div class="section" id="polymorphic-models-for-django">
<h1>Polymorphic Models for Django</h1> <h1>Polymorphic Models for Django</h1>
<p>.</p>
<div class="section" id="quick-start-docs-contributing"> <div class="section" id="quick-start-docs-contributing">
<h2>Quick Start, Docs, Contributing</h2> <h2>Quick Start, Docs, Contributing</h2>
<ul class="simple"> <ul class="simple">
-1
View File
@@ -1,7 +1,6 @@
Polymorphic Models for Django Polymorphic Models for Django
============================= =============================
.
Quick Start, Docs, Contributing Quick Start, Docs, Contributing
------------------------------- -------------------------------
+3 -1
View File
@@ -92,9 +92,11 @@ class PolymorphicModelBase(ModelBase):
for key, manager in base.__dict__.items(): for key, manager in base.__dict__.items():
if type(manager) == models.manager.ManagerDescriptor: manager = manager.manager if type(manager) == models.manager.ManagerDescriptor: manager = manager.manager
if not isinstance(manager, models.Manager): continue 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 attrs: continue
if key in add_managers_keys: continue # manager with that name already added, skip 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 if isinstance(manager, PolymorphicManager): # validate any inherited polymorphic managers
self.validate_model_manager(manager, self.__name__, key) self.validate_model_manager(manager, self.__name__, key)
add_managers.append((base.__name__, key, manager)) add_managers.append((base.__name__, key, manager))
+4 -5
View File
@@ -135,25 +135,24 @@ class PolymorphicModel(models.Model):
our appropriate base_objects manager. our appropriate base_objects manager.
""" """
super(PolymorphicModel, self).__init__(*args, ** kwargs) super(PolymorphicModel, self).__init__(*args, ** kwargs)
if self.__class__.polymorphic_super_sub_accessors_replaced: return if self.__class__.polymorphic_super_sub_accessors_replaced: return
self.__class__.polymorphic_super_sub_accessors_replaced = True 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): def accessor_function(self):
attr = model.base_objects.get(pk=self.pk) attr = model.base_objects.get(pk=self.pk)
return attr return attr
return accessor_function return accessor_function
subclasses_and_superclasses_accessors = self.get_inheritance_relation_fields_and_models() 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 from django.db.models.fields.related import SingleRelatedObjectDescriptor, ReverseSingleRelatedObjectDescriptor
for name,model in subclasses_and_superclasses_accessors.iteritems(): for name,model in subclasses_and_superclasses_accessors.iteritems():
orig_accessor = getattr(self.__class__, name, None) orig_accessor = getattr(self.__class__, name, None)
if type(orig_accessor) in [SingleRelatedObjectDescriptor,ReverseSingleRelatedObjectDescriptor]: if type(orig_accessor) in [SingleRelatedObjectDescriptor,ReverseSingleRelatedObjectDescriptor]:
#print 'replacing',name, orig_accessor #print >>sys.stderr, '---------- replacing',name, orig_accessor
setattr(self.__class__, name, property(create_accessor_function_for_model(model)) ) setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)) )
def get_inheritance_relation_fields_and_models(self): def get_inheritance_relation_fields_and_models(self):
"""helper function for __init__: """helper function for __init__:
+70 -12
View File
@@ -4,6 +4,7 @@
""" """
import settings import settings
import sys
from django.test import TestCase from django.test import TestCase
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
@@ -29,6 +30,8 @@ class Model2B(Model2A):
field2 = models.CharField(max_length=10) field2 = models.CharField(max_length=10)
class Model2C(Model2B): class Model2C(Model2B):
field3 = models.CharField(max_length=10) field3 = models.CharField(max_length=10)
class Model2D(Model2C):
field4 = models.CharField(max_length=10)
class ModelShow1(ShowFieldType,PolymorphicModel): class ModelShow1(ShowFieldType,PolymorphicModel):
field1 = models.CharField(max_length=10) field1 = models.CharField(max_length=10)
@@ -84,6 +87,13 @@ class RelationBC(RelationB):
class RelatingModel(models.Model): class RelatingModel(models.Model):
many2many = models.ManyToManyField(Model2A) 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): class MyManager(PolymorphicManager):
def get_query_set(self): def get_query_set(self):
return super(MyManager, self).get_query_set().order_by('-field1') 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') entry1 = BlogEntry_limit_choices_to.objects.create(blog=blog_b, text='bla2')
entry2 = 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": """ __test__ = {"doctest": """
####################################################### #######################################################
@@ -249,19 +262,18 @@ __test__ = {"doctest": """
>>> settings.DEBUG=True >>> settings.DEBUG=True
#>>> get_version()
#'1.0 rc1'
### simple inheritance ### simple inheritance
>>> o=Model2A.objects.create(field1='A1') >>> o=Model2A.objects.create(field1='A1')
>>> o=Model2B.objects.create(field1='B1', field2='B2') >>> o=Model2B.objects.create(field1='B1', field2='B2')
>>> o=Model2C.objects.create(field1='C1', field2='C2', field3='C3') >>> o=Model2C.objects.create(field1='C1', field2='C2', field3='C3')
>>> o=Model2D.objects.create(field1='D1', field2='D2', field3='D3', field4='D4')
[ <Model2A: id 1, field1 (CharField)>, >>> Model2A.objects.all()
[ <Model2A: id 1, field1 (CharField)>,
<Model2B: id 2, field1 (CharField), field2 (CharField)>, <Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ] <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>,
<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField)> ]
# manual get_real_instance() # manual get_real_instance()
>>> o=Model2A.base_objects.get(field1='C1') >>> o=Model2A.base_objects.get(field1='C1')
@@ -269,6 +281,48 @@ __test__ = {"doctest": """
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
### test inheritance pointers & _base_managers
>>> show_base_manager(PlainA)
<class 'django.db.models.manager.Manager'> <class 'polymorphic.tests.PlainA'>
>>> show_base_manager(PlainB)
<class 'django.db.models.manager.Manager'> <class 'polymorphic.tests.PlainB'>
>>> show_base_manager(PlainC)
<class 'django.db.models.manager.Manager'> <class 'polymorphic.tests.PlainC'>
>>> show_base_manager(Model2A)
<class 'polymorphic.manager.PolymorphicManager'> <class 'polymorphic.tests.Model2A'>
>>> show_base_manager(Model2B)
<class 'django.db.models.manager.Manager'> <class 'polymorphic.tests.Model2B'>
>>> show_base_manager(Model2C)
<class 'django.db.models.manager.Manager'> <class 'polymorphic.tests.Model2C'>
>>> show_base_manager(One2OneRelatingModel)
<class 'polymorphic.manager.PolymorphicManager'> <class 'polymorphic.tests.One2OneRelatingModel'>
>>> show_base_manager(One2OneRelatingModelDerived)
<class 'django.db.models.manager.Manager'> <class 'polymorphic.tests.One2OneRelatingModelDerived'>
>>> o=Model2A.base_objects.get(field1='C1')
>>> o.model2b
<Model2B: id 3, field1 (CharField), field2 (CharField)>
>>> o=Model2B.base_objects.get(field1='C1')
>>> o.model2c
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
### 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
<Model2A: id 3, field1 (CharField)>
>>> c=One2OneRelatingModelDerived.objects.get(field1='f1')
>>> c.one2one
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
>>> a.one2onerelatingmodel
<One2OneRelatingModelDerived: One2OneRelatingModelDerived object>
### ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent, also with annotate() ### ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent, also with annotate()
>>> o=ModelShow1.objects.create(field1='abc') >>> o=ModelShow1.objects.create(field1='abc')
@@ -315,15 +369,18 @@ __test__ = {"doctest": """
>>> Model2A.objects.instance_of(Model2B) >>> Model2A.objects.instance_of(Model2B)
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>, [ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ] <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>,
<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField)> ]
>>> Model2A.objects.filter(instance_of=Model2B) >>> Model2A.objects.filter(instance_of=Model2B)
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>, [ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ] <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>,
<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField)> ]
>>> Model2A.objects.filter(Q(instance_of=Model2B)) >>> Model2A.objects.filter(Q(instance_of=Model2B))
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>, [ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ] <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>,
<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField)> ]
>>> Model2A.objects.not_instance_of(Model2B) >>> Model2A.objects.not_instance_of(Model2B)
[ <Model2A: id 1, field1 (CharField)> ] [ <Model2A: id 1, field1 (CharField)> ]
@@ -345,7 +402,8 @@ __test__ = {"doctest": """
>>> oa.delete() >>> oa.delete()
>>> Model2A.objects.all() >>> Model2A.objects.all()
[ <Model2A: id 1, field1 (CharField)>, [ <Model2A: id 1, field1 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ] <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>,
<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField)> ]
### queryset combining ### queryset combining
@@ -405,8 +463,8 @@ __test__ = {"doctest": """
>>> o=ModelWithMyManager.objects.create(field1='D1b', field4='D4b') >>> o=ModelWithMyManager.objects.create(field1='D1b', field4='D4b')
>>> ModelWithMyManager.objects.all() >>> ModelWithMyManager.objects.all()
[ <ModelWithMyManager: id 5, field1 (CharField): "D1b", field4 (CharField): "D4b">, [ <ModelWithMyManager: id 6, field1 (CharField): "D1b", field4 (CharField): "D4b">,
<ModelWithMyManager: id 4, field1 (CharField): "D1a", field4 (CharField): "D4a"> ] <ModelWithMyManager: id 5, field1 (CharField): "D1a", field4 (CharField): "D4a"> ]
>>> type(ModelWithMyManager.objects) >>> type(ModelWithMyManager.objects)
<class 'polymorphic.tests.MyManager'> <class 'polymorphic.tests.MyManager'>