Merge pull request #31 from vdboor/django15

Fix Django 1.5 (proxy models broke polymorphism)
fix_request_path_info
Diederik van der Boor 2013-04-07 14:33:49 -07:00
commit 38a230891c
3 changed files with 70 additions and 4 deletions

View File

@ -16,6 +16,11 @@ from query import PolymorphicQuerySet
# These are forbidden as field names (a descriptive exception is raised) # These are forbidden as field names (a descriptive exception is raised)
POLYMORPHIC_SPECIAL_Q_KWORDS = ['instance_of', 'not_instance_of'] POLYMORPHIC_SPECIAL_Q_KWORDS = ['instance_of', 'not_instance_of']
try:
from django.db.models.manager import AbstractManagerDescriptor # Django 1.5
except ImportError:
AbstractManagerDescriptor = None
################################################################################### ###################################################################################
### PolymorphicModel meta class ### PolymorphicModel meta class
@ -91,6 +96,7 @@ class PolymorphicModelBase(ModelBase):
use correct mro, only use managers with _inherited==False (they are of no use), use correct mro, only use managers with _inherited==False (they are of no use),
skip managers that are overwritten by the user with same-named class attributes (in attrs) skip managers that are overwritten by the user with same-named class attributes (in attrs)
""" """
#print "** ", self.__name__
add_managers = [] add_managers = []
add_managers_keys = set() add_managers_keys = set()
for base in self.__mro__[1:]: for base in self.__mro__[1:]:
@ -102,9 +108,23 @@ class PolymorphicModelBase(ModelBase):
for key, manager in base.__dict__.items(): for key, manager in base.__dict__.items():
if type(manager) == models.manager.ManagerDescriptor: if type(manager) == models.manager.ManagerDescriptor:
manager = manager.manager manager = manager.manager
if AbstractManagerDescriptor is not None:
# Django 1.4 unconditionally assigned managers to a model. As of Django 1.5 however,
# the abstract models don't get any managers, only a AbstractManagerDescriptor as substitute.
# Pretend that the manager is still there, so all code works like it used to.
if type(manager) == AbstractManagerDescriptor and base.__name__ == 'PolymorphicModel':
model = manager.model
if key == 'objects':
manager = PolymorphicManager()
manager.model = model
elif key == 'base_objects':
manager = models.Manager()
manager.model = model
if not isinstance(manager, models.Manager): if not isinstance(manager, models.Manager):
continue continue
if key in ['_base_manager']: if key == '_base_manager':
continue # let Django handle _base_manager continue # let Django handle _base_manager
if key in attrs: if key in attrs:
continue continue
@ -112,7 +132,8 @@ class PolymorphicModelBase(ModelBase):
continue # manager with that name already added, skip continue # manager with that name already added, skip
if manager._inherited: if manager._inherited:
continue # inherited managers (on the bases) have no significance, they are just copies continue # inherited managers (on the bases) have no significance, they are just copies
#print >>sys.stderr,'##',self.__name__, key #print '## {0} {1}'.format(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))
@ -121,16 +142,18 @@ class PolymorphicModelBase(ModelBase):
@classmethod @classmethod
def get_first_user_defined_manager(self): def get_first_user_defined_manager(self):
# See if there is a manager attribute directly stored at this inheritance level.
mgr_list = [] mgr_list = []
for key, val in self.__dict__.items(): for key, val in self.__dict__.items():
item = getattr(self, key) item = getattr(self, key)
if not isinstance(item, models.Manager): continue if not isinstance(item, models.Manager): continue
mgr_list.append((item.creation_counter, key, item)) mgr_list.append((item.creation_counter, key, item))
# if there are user defined managers, use first one as _default_manager # if there are user defined managers, use first one as _default_manager
if mgr_list: if mgr_list:
_, manager_name, manager = sorted(mgr_list)[0] _, manager_name, manager = sorted(mgr_list)[0]
#sys.stderr.write( '\n# first user defined manager for model "{model}":\n# "{mgrname}": {mgr}\n# manager model: {mgrmodel}\n\n' #sys.stderr.write( '\n# first user defined manager for model "{model}":\n# "{mgrname}": {mgr}\n# manager model: {mgrmodel}\n\n'
# .format( model=model_name, mgrname=manager_name, mgr=manager, mgrmodel=manager.model ) ) # .format( model=self.__name__, mgrname=manager_name, mgr=manager, mgrmodel=manager.model ) )
return manager return manager
return None return None

View File

@ -67,6 +67,8 @@ class PolymorphicModel(models.Model):
# some applications want to know the name of the fields that are added to its models # some applications want to know the name of the fields that are added to its models
polymorphic_internal_model_fields = ['polymorphic_ctype'] polymorphic_internal_model_fields = ['polymorphic_ctype']
# Note that Django 1.5 removes these managers because the model is abstract.
# They are pretended to be there by the metaclass in PolymorphicModelBase.get_inherited_managers()
objects = PolymorphicManager() objects = PolymorphicManager()
base_objects = models.Manager() base_objects = models.Manager()

View File

@ -178,6 +178,17 @@ class UUIDPlainB(UUIDPlainA):
class UUIDPlainC(UUIDPlainB): class UUIDPlainC(UUIDPlainB):
field3 = models.CharField(max_length=10) field3 = models.CharField(max_length=10)
# base -> proxy -> real models
class ProxiedBase(ShowFieldTypeAndContent, PolymorphicModel):
name = models.CharField(max_length=10)
class ProxyModelBase(ProxiedBase):
class Meta:
proxy = True
class ProxyModelA(ProxyModelBase):
field1 = models.CharField(max_length=10)
class ProxyModelB(ProxyModelBase):
field2 = models.CharField(max_length=10)
# test bad field name # test bad field name
#class TestBadFieldModel(ShowFieldType, PolymorphicModel): #class TestBadFieldModel(ShowFieldType, PolymorphicModel):
@ -194,7 +205,6 @@ class PolymorphicTests(TestCase):
""" """
The test suite The test suite
""" """
def test_diamond_inheritance(self): def test_diamond_inheritance(self):
# Django diamond problem # Django diamond problem
o1 = DiamondXY.objects.create(field_b='b', field_x='x', field_y='y') o1 = DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')
@ -613,6 +623,37 @@ class PolymorphicTests(TestCase):
self.assertEqual(repr(type(MROBase2._default_manager)), "<class 'polymorphic.manager.PolymorphicManager'>") self.assertEqual(repr(type(MROBase2._default_manager)), "<class 'polymorphic.manager.PolymorphicManager'>")
def test_proxy_model_inheritance(self):
"""
Polymorphic abilities should also work when the base model is a proxy object.
"""
# The managers should point to the proper objects.
# otherwise, the whole excersise is pointless.
self.assertEqual(ProxiedBase.objects.model, ProxiedBase)
self.assertEqual(ProxyModelBase.objects.model, ProxyModelBase)
self.assertEqual(ProxyModelA.objects.model, ProxyModelA)
self.assertEqual(ProxyModelB.objects.model, ProxyModelB)
# Create objects
ProxyModelA.objects.create(name="object1")
ProxyModelB.objects.create(name="object2", field2="bb")
# Getting single objects
object1 = ProxyModelBase.objects.get(name='object1')
object2 = ProxyModelBase.objects.get(name='object2')
self.assertEqual(repr(object1), '<ProxyModelA: id 1, name (CharField) "object1", field1 (CharField) "">')
self.assertEqual(repr(object2), '<ProxyModelB: id 2, name (CharField) "object2", field2 (CharField) "bb">')
self.assertIsInstance(object1, ProxyModelA)
self.assertIsInstance(object2, ProxyModelB)
# Same for lists
objects = list(ProxyModelBase.objects.all().order_by('name'))
self.assertEqual(repr(objects[0]), '<ProxyModelA: id 1, name (CharField) "object1", field1 (CharField) "">')
self.assertEqual(repr(objects[1]), '<ProxyModelB: id 2, name (CharField) "object2", field2 (CharField) "bb">')
self.assertIsInstance(objects[0], ProxyModelA)
self.assertIsInstance(objects[1], ProxyModelB)
def test_fix_getattribute(self): def test_fix_getattribute(self):
### fixed issue in PolymorphicModel.__getattribute__: field name same as model name ### fixed issue in PolymorphicModel.__getattribute__: field name same as model name
o = ModelFieldNameTest.objects.create(modelfieldnametest='1') o = ModelFieldNameTest.objects.create(modelfieldnametest='1')