Merge pull request #31 from vdboor/django15
Fix Django 1.5 (proxy models broke polymorphism)fix_request_path_info
commit
38a230891c
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue