- renamed ShowField* to ShowFieldType, ShowFieldContent, ShowFieldTypeAndContent, in order to reflect better what they do.

- by default, django_polymorphic's pretty printing of querysets/objects (via ShowField*) is not used anymore
- ShowField mixins now also show the annotations (after the regular fields, prepended by "Ann:")
- cleaned up implementation.
fix_request_path_info
Bert Constantin 2010-10-16 14:40:27 +02:00
parent 50b54f5aca
commit 01bdb2f9b0
8 changed files with 203 additions and 96 deletions

3
.gitignore vendored
View File

@ -15,11 +15,14 @@ pushhg
pushreg pushreg
pbackup pbackup
mcmd.py mcmd.py
dbconfig_local.py
pip-log.txt pip-log.txt
build build
ppreadme.py ppreadme.py
ppdocs.py ppdocs.py
common.css
screen.css
README.html README.html
DOCS.html DOCS.html

View File

@ -22,13 +22,15 @@ class Command(NoArgsCommand):
def handle_noargs(self, **options): def handle_noargs(self, **options):
print 'polycmd - sqlite test db is stored in:',settings.DATABASE_NAME print 'polycmd - sqlite test db is stored in:',settings.DATABASE_NAME
print print
"""
ModelA.objects.all().delete() ModelA.objects.all().delete()
o=ModelA.objects.create(field1='A1') o=ModelA.objects.create(field1='A1')
o=ModelB.objects.create(field1='B1', field2='B2') o=ModelB.objects.create(field1='B1', field2='B2')
o=ModelC.objects.create(field1='C1', field2='C2', field3='C3') o=ModelC.objects.create(field1='C1', field2='C2', field3='C3')
print ModelA.objects.all() print ModelA.objects.all()
print print
"""
Project.objects.all().delete() Project.objects.all().delete()
o=Project.objects.create(topic="John's gathering") o=Project.objects.create(topic="John's gathering")

View File

@ -2,29 +2,24 @@
from django.db import models from django.db import models
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
class Project(ShowFields, PolymorphicModel): class Project(ShowFieldContent, PolymorphicModel):
topic = models.CharField(max_length=30) topic = models.CharField(max_length=30)
class ArtProject(Project): class ArtProject(Project):
artist = models.CharField(max_length=30) artist = models.CharField(max_length=30)
class ResearchProject(Project): class ResearchProject(Project):
supervisor = models.CharField(max_length=30) supervisor = models.CharField(max_length=30)
class ModelA(PolymorphicModel): class ModelA(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10) field1 = models.CharField(max_length=10)
class ModelB(ModelA): class ModelB(ModelA):
field2 = models.CharField(max_length=10) field2 = models.CharField(max_length=10)
class ModelC(ModelB): class ModelC(ModelB):
field3 = models.CharField(max_length=10) field3 = models.CharField(max_length=10)
class SModelA(ShowFieldsAndTypes, PolymorphicModel):
field1 = models.CharField(max_length=10)
class SModelB(SModelA):
field2 = models.CharField(max_length=10)
class SModelC(SModelB):
field3 = models.CharField(max_length=10)
# for Django 1.2+, test models with same names in different apps # for Django 1.2+, test models with same names in different apps
# (the other models with identical names are in polymorphic/tests.py) # (the other models with identical names are in polymorphic/tests.py)

View File

@ -10,7 +10,8 @@ Please see LICENSE and AUTHORS for more information.
from polymorphic_model import PolymorphicModel from polymorphic_model import PolymorphicModel
from manager import PolymorphicManager from manager import PolymorphicManager
from query import PolymorphicQuerySet from query import PolymorphicQuerySet
from showfields import ShowFields, ShowFieldsAndTypes from showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
#from showfields import ShowFieldTypes, ShowFields, ShowFieldsAndTypes # import old names for compatibility
VERSION = (0, 5, 0, 'beta') VERSION = (0, 5, 0, 'beta')

View File

@ -26,13 +26,13 @@ from django import VERSION as django_VERSION
from base import PolymorphicModelBase from base import PolymorphicModelBase
from manager import PolymorphicManager from manager import PolymorphicManager
from query import PolymorphicQuerySet from query import PolymorphicQuerySet
from showfields import ShowFieldTypes from showfields import ShowFieldType
################################################################################### ###################################################################################
### PolymorphicModel ### PolymorphicModel
class PolymorphicModel(ShowFieldTypes, models.Model): class PolymorphicModel(models.Model):
""" """
Abstract base class that provides polymorphic behaviour Abstract base class that provides polymorphic behaviour
for any model directly or indirectly derived from it. for any model directly or indirectly derived from it.
@ -53,7 +53,11 @@ class PolymorphicModel(ShowFieldTypes, models.Model):
""" """
__metaclass__ = PolymorphicModelBase __metaclass__ = PolymorphicModelBase
polymorphic_model_marker = True # for PolymorphicModelBase # for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing)
polymorphic_model_marker = True
# for PolymorphicQuery, True => an overloaded __repr__ with nicer multi-line output is used by PolymorphicQuery
polymorphic_query_multiline_output = False
class Meta: class Meta:
abstract = True abstract = True
@ -66,7 +70,7 @@ class PolymorphicModel(ShowFieldTypes, models.Model):
p_related_name_template = 'polymorphic_%(app_label)s.%(class)s_set' p_related_name_template = 'polymorphic_%(app_label)s.%(class)s_set'
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False, polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
related_name=p_related_name_template) related_name=p_related_name_template)
# 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' ]
@ -130,10 +134,12 @@ class PolymorphicModel(ShowFieldTypes, models.Model):
name = model.__name__.lower() name = model.__name__.lower()
if as_ptr: name+='_ptr' if as_ptr: name+='_ptr'
result[name] = model result[name] = model
def add_all_base_models(model, result): def add_all_base_models(model, result):
add_if_regular_sub_or_super_class(model, True, result) add_if_regular_sub_or_super_class(model, True, result)
for b in model.__bases__: for b in model.__bases__:
add_all_base_models(b, result) add_all_base_models(b, result)
def add_sub_models(model, result): def add_sub_models(model, result):
for b in model.__subclasses__(): for b in model.__subclasses__():
add_if_regular_sub_or_super_class(b, False, result) add_if_regular_sub_or_super_class(b, False, result)
@ -150,6 +156,6 @@ class PolymorphicModel(ShowFieldTypes, models.Model):
attr = model.base_objects.get(id=id) attr = model.base_objects.get(id=id)
#print '---',self.__class__.__name__,name #print '---',self.__class__.__name__,name
return attr return attr
return super(PolymorphicModel, self).__getattribute__(name) return super(PolymorphicModel, self).__getattribute__(name)

View File

@ -144,6 +144,7 @@ class PolymorphicQuerySet(QuerySet):
for modelclass, idlist in idlist_per_model.items(): for modelclass, idlist in idlist_per_model.items():
qs = modelclass.base_objects.filter(id__in=idlist) qs = modelclass.base_objects.filter(id__in=idlist)
qs.dup_select_related(self) # copy select related configuration to new qs qs.dup_select_related(self) # copy select related configuration to new qs
for o in qs: for o in qs:
if self.query.aggregates: if self.query.aggregates:
for anno in self.query.aggregates.keys(): for anno in self.query.aggregates.keys():
@ -153,6 +154,13 @@ class PolymorphicQuerySet(QuerySet):
# re-create correct order and return result list # re-create correct order and return result list
resultlist = [ results[ordered_id] for ordered_id in ordered_id_list if ordered_id in results ] resultlist = [ results[ordered_id] for ordered_id in ordered_id_list if ordered_id in results ]
# set polymorphic_annotate_names in all objects (currently just used for debugging/printing)
if self.query.aggregates:
annotate_names=self.query.aggregates.keys() # get annotate fields list
for o in resultlist:
o.polymorphic_annotate_names=annotate_names
return resultlist return resultlist
def iterator(self): def iterator(self):
@ -182,7 +190,9 @@ class PolymorphicQuerySet(QuerySet):
reached_end = False reached_end = False
for i in range(Polymorphic_QuerySet_objects_per_request): for i in range(Polymorphic_QuerySet_objects_per_request):
try: base_result_objects.append(base_iter.next()) try:
o=base_iter.next()
base_result_objects.append(o)
except StopIteration: except StopIteration:
reached_end = True reached_end = True
break break
@ -194,8 +204,10 @@ class PolymorphicQuerySet(QuerySet):
if reached_end: raise StopIteration if reached_end: raise StopIteration
def __repr__(self): def __repr__(self, *args, **kwargs):
result = [ repr(o) for o in self.all() ] if self.model.polymorphic_query_multiline_output:
return '[ ' + ',\n '.join(result) + ' ]' result = [ repr(o) for o in self.all() ]
return '[ ' + ',\n '.join(result) + ' ]'
else:
return super(PolymorphicQuerySet,self).__repr__(*args, **kwargs)

View File

@ -2,49 +2,67 @@
from django.db import models from django.db import models
def _represent_foreign_key(o):
if o is None:
out = '"None"'
else:
out = '"' + o.__class__.__name__ + '"'
return out
class ShowFieldsAndTypes(object): class ShowFieldBase(object):
""" model mixin, like ShowFields, but also show field types """ """ base class for the ShowField... model mixins, does the work """
def __repr__(self): polymorphic_query_multiline_output = True # cause nicer multiline PolymorphicQuery output
out = 'id ' + str(self.pk)
for f in self._meta.fields: polymorphic_showfield_type = False
if f.name in [ 'id' ] + self.polymorphic_internal_model_fields or 'ptr' in f.name: continue polymorphic_showfield_content = False
out += ', ' + f.name + ' (' + type(f).__name__ + ')'
if isinstance(f, (models.ForeignKey)):
o = getattr(self, f.name)
out += ': ' + _represent_foreign_key(o)
else:
out += ': "' + getattr(self, f.name) + '"'
return '<' + self.__class__.__name__ + ': ' + out + '>'
class ShowFields(object):
""" model mixin that shows the object's class, it's fields and field contents """
def __repr__(self): def __repr__(self):
out = 'id ' + str(self.pk) + ', ' return self.__unicode__()
for f in self._meta.fields:
def __unicode__(self):
out = u'<'+self.__class__.__name__+': id %s' % unicode(self.pk)
for f in self._meta.fields + self._meta.many_to_many:
if f.name in [ 'id' ] + self.polymorphic_internal_model_fields or 'ptr' in f.name: continue if f.name in [ 'id' ] + self.polymorphic_internal_model_fields or 'ptr' in f.name: continue
out += ', ' + f.name out += ', ' + f.name
if isinstance(f, (models.ForeignKey)):
if self.polymorphic_showfield_type:
out += ' (' + type(f).__name__ + ')'
if self.polymorphic_showfield_content:
o = getattr(self, f.name) o = getattr(self, f.name)
out += ': ' + _represent_foreign_key(o)
else:
out += ': "' + getattr(self, f.name) + '"'
return '<' + (self.__class__.__name__ + ': ') + out + '>'
class ShowFieldTypes(object): if isinstance(f, (models.ForeignKey)):
""" INTERNAL; don't use this! out += ': ' + ( '"None"' if o is None else '"' + o.__class__.__name__ + '"' )
This mixin is already used by default by PolymorphicModel.
(model mixin that shows the object's class and it's field types) """
def __repr__(self):
out = self.__class__.__name__ + ': id ' + str(self.pk)
for f in self._meta.fields:
if f.name in [ 'id' ] + self.polymorphic_internal_model_fields or 'ptr' in f.name: continue
out += ', ' + f.name + ' (' + type(f).__name__ + ')'
return '<' + out + '>'
elif isinstance(f, (models.ManyToManyField)):
out += ': %d' % o.count()
else:
out += ': "' + unicode(o) + '"'
if hasattr(self,'polymorphic_annotate_names'):
out += ' - Ann: '
for an in self.polymorphic_annotate_names:
if an != self.polymorphic_annotate_names[0]:
out += ', '
out += an
if self.polymorphic_showfield_type:
out += ' (' + type(getattr(self, an)).__name__ + ')'
if self.polymorphic_showfield_content:
out += ': "' + unicode(getattr(self, an)) + '"'
return out+'>'
class ShowFieldType(ShowFieldBase):
""" model mixin that shows the object's class and it's field types """
polymorphic_showfield_type = True
class ShowFieldContent(ShowFieldBase):
""" model mixin that shows the object's class, it's fields and field contents """
polymorphic_showfield_content = True
class ShowFieldTypeAndContent(ShowFieldBase):
""" model mixin, like ShowFieldContent, but also show field types """
polymorphic_showfield_type = True
polymorphic_showfield_content = True
# compatibility with old class names
ShowFieldTypes = ShowFieldType
ShowFields = ShowFieldContent
ShowFieldsAndTypes = ShowFieldTypeAndContent

View File

@ -7,11 +7,12 @@ import settings
from django.test import TestCase from django.test import TestCase
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.db.models import Q from django.db.models import Q,Count
from django.db import models from django.db import models
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from pprint import pprint
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFields, ShowFieldsAndTypes, get_version from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet, ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent, get_version
class PlainA(models.Model): class PlainA(models.Model):
field1 = models.CharField(max_length=10) field1 = models.CharField(max_length=10)
@ -20,14 +21,24 @@ class PlainB(PlainA):
class PlainC(PlainB): class PlainC(PlainB):
field3 = models.CharField(max_length=10) field3 = models.CharField(max_length=10)
class Model2A(PolymorphicModel): class Model2A(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10) field1 = models.CharField(max_length=10)
class Model2B(Model2A): 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 Base(PolymorphicModel): class ModelShow1(ShowFieldType,PolymorphicModel):
field1 = models.CharField(max_length=10)
m2m = models.ManyToManyField('self')
class ModelShow2(ShowFieldContent, PolymorphicModel):
field1 = models.CharField(max_length=10)
m2m = models.ManyToManyField('self')
class ModelShow3(ShowFieldTypeAndContent, PolymorphicModel):
field1 = models.CharField(max_length=10)
m2m = models.ManyToManyField('self')
class Base(ShowFieldType, PolymorphicModel):
field_b = models.CharField(max_length=10) field_b = models.CharField(max_length=10)
class ModelX(Base): class ModelX(Base):
field_x = models.CharField(max_length=10) field_x = models.CharField(max_length=10)
@ -36,7 +47,7 @@ class ModelY(Base):
class Enhance_Plain(models.Model): class Enhance_Plain(models.Model):
field_p = models.CharField(max_length=10) field_p = models.CharField(max_length=10)
class Enhance_Base(ShowFieldsAndTypes, PolymorphicModel): class Enhance_Base(ShowFieldTypeAndContent, PolymorphicModel):
field_b = models.CharField(max_length=10) field_b = models.CharField(max_length=10)
class Enhance_Inherit(Enhance_Base, Enhance_Plain): class Enhance_Inherit(Enhance_Base, Enhance_Plain):
field_i = models.CharField(max_length=10) field_i = models.CharField(max_length=10)
@ -51,7 +62,7 @@ class DiamondY(DiamondBase):
class DiamondXY(DiamondX, DiamondY): class DiamondXY(DiamondX, DiamondY):
pass pass
class RelationBase(ShowFieldsAndTypes, PolymorphicModel): class RelationBase(ShowFieldTypeAndContent, PolymorphicModel):
field_base = models.CharField(max_length=10) field_base = models.CharField(max_length=10)
fk = models.ForeignKey('self', null=True) fk = models.ForeignKey('self', null=True)
m2m = models.ManyToManyField('self') m2m = models.ManyToManyField('self')
@ -68,11 +79,11 @@ class RelatingModel(models.Model):
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')
class ModelWithMyManager(ShowFieldsAndTypes, Model2A): class ModelWithMyManager(ShowFieldTypeAndContent, Model2A):
objects = MyManager() objects = MyManager()
field4 = models.CharField(max_length=10) field4 = models.CharField(max_length=10)
class MROBase1(PolymorphicModel): class MROBase1(ShowFieldType, PolymorphicModel):
objects = MyManager() objects = MyManager()
field1 = models.CharField(max_length=10) # needed as MyManager uses it field1 = models.CharField(max_length=10) # needed as MyManager uses it
class MROBase2(MROBase1): class MROBase2(MROBase1):
@ -89,23 +100,23 @@ class MgrInheritA(models.Model):
class MgrInheritB(MgrInheritA): class MgrInheritB(MgrInheritA):
mgrB = models.Manager() mgrB = models.Manager()
field2 = models.CharField(max_length=10) field2 = models.CharField(max_length=10)
class MgrInheritC(ShowFieldsAndTypes, MgrInheritB): class MgrInheritC(ShowFieldTypeAndContent, MgrInheritB):
pass pass
class BlogBase(ShowFieldsAndTypes, PolymorphicModel): class BlogBase(ShowFieldTypeAndContent, PolymorphicModel):
name = models.CharField(max_length=10) name = models.CharField(max_length=10)
class BlogA(BlogBase): class BlogA(BlogBase):
info = models.CharField(max_length=10) info = models.CharField(max_length=10)
class BlogB(BlogBase): class BlogB(BlogBase):
pass pass
class BlogA_Entry(ShowFieldsAndTypes, PolymorphicModel): class BlogEntry(ShowFieldTypeAndContent, PolymorphicModel):
blog = models.ForeignKey(BlogA) blog = models.ForeignKey(BlogA)
text = models.CharField(max_length=10) text = models.CharField(max_length=10)
class ModelFieldNameTest(PolymorphicModel): class ModelFieldNameTest(ShowFieldType, PolymorphicModel):
modelfieldnametest = models.CharField(max_length=10) modelfieldnametest = models.CharField(max_length=10)
class InitTestModel(PolymorphicModel): class InitTestModel(ShowFieldType, PolymorphicModel):
bar = models.CharField(max_length=100) bar = models.CharField(max_length=100)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs['bar'] = self.x() kwargs['bar'] = self.x()
@ -115,13 +126,13 @@ class InitTestModelSubclass(InitTestModel):
return 'XYZ' return 'XYZ'
# test bad field name # test bad field name
#class TestBadFieldModel(PolymorphicModel): #class TestBadFieldModel(ShowFieldType, PolymorphicModel):
# instance_of = models.CharField(max_length=10) # instance_of = models.CharField(max_length=10)
# validation error: "polymorphic.relatednameclash: Accessor for field 'polymorphic_ctype' clashes # validation error: "polymorphic.relatednameclash: Accessor for field 'polymorphic_ctype' clashes
# with related field 'ContentType.relatednameclash_set'." (reported by Andrew Ingram) # with related field 'ContentType.relatednameclash_set'." (reported by Andrew Ingram)
# fixed with related_name # fixed with related_name
class RelatedNameClash(PolymorphicModel): class RelatedNameClash(ShowFieldType, PolymorphicModel):
ctype = models.ForeignKey(ContentType, null=True, editable=False) ctype = models.ForeignKey(ContentType, null=True, editable=False)
@ -135,22 +146,29 @@ class testclass(TestCase):
if o.field_b != 'b': print '# Django model inheritance diamond problem detected' if o.field_b != 'b': print '# Django model inheritance diamond problem detected'
def test_annotate_aggregate_order(self): def test_annotate_aggregate_order(self):
from django.db.models import Count
BlogA.objects.all().delete() # create a blog of type BlogA
blog = BlogA.objects.create(name='B1', info='i1') blog = BlogA.objects.create(name='B1', info='i1')
entry1 = blog.bloga_entry_set.create(text='bla') # create two blog entries in BlogA
entry2 = BlogA_Entry.objects.create(blog=blog, text='bla2') entry1 = blog.blogentry_set.create(text='bla')
entry2 = BlogEntry.objects.create(blog=blog, text='bla2')
# create some BlogB to make the table more diverse # create some blogs of type BlogB to make the BlogBase table data really polymorphic
o = BlogB.objects.create(name='Bb1') o = BlogB.objects.create(name='Bb1')
o = BlogB.objects.create(name='Bb2') o = BlogB.objects.create(name='Bb2')
o = BlogB.objects.create(name='Bb3') o = BlogB.objects.create(name='Bb3')
qs = BlogBase.objects.annotate(entrycount=Count('BlogA___bloga_entry')) qs = BlogBase.objects.annotate(entrycount=Count('BlogA___blogentry'))
assert qs[0].entrycount == 2
x = BlogBase.objects.aggregate(entrycount=Count('BlogA___bloga_entry')) assert len(qs)==4
for o in qs:
if o.name=='B1':
assert o.entrycount == 2
else:
assert o.entrycount == 0
x = BlogBase.objects.aggregate(entrycount=Count('BlogA___blogentry'))
assert x['entrycount'] == 2 assert x['entrycount'] == 2
# create some more blogs for next test # create some more blogs for next test
@ -159,7 +177,8 @@ class testclass(TestCase):
b2 = BlogA.objects.create(name='B4', info='i4') b2 = BlogA.objects.create(name='B4', info='i4')
b2 = BlogA.objects.create(name='B5', info='i5') b2 = BlogA.objects.create(name='B5', info='i5')
# test ordering ### test ordering for field in all entries
expected = ''' expected = '''
[ <BlogB: id 4, name (CharField): "Bb3">, [ <BlogB: id 4, name (CharField): "Bb3">,
<BlogB: id 3, name (CharField): "Bb2">, <BlogB: id 3, name (CharField): "Bb2">,
@ -171,8 +190,11 @@ class testclass(TestCase):
<BlogA: id 1, name (CharField): "B1", info (CharField): "i1"> ]''' <BlogA: id 1, name (CharField): "B1", info (CharField): "i1"> ]'''
x = '\n' + repr(BlogBase.objects.order_by('-name')) x = '\n' + repr(BlogBase.objects.order_by('-name'))
assert x == expected assert x == expected
expected=''' ### test ordering for field in one subclass only
# MySQL and SQLite return this order
expected1='''
[ <BlogA: id 8, name (CharField): "B5", info (CharField): "i5">, [ <BlogA: id 8, name (CharField): "B5", info (CharField): "i5">,
<BlogA: id 7, name (CharField): "B4", info (CharField): "i4">, <BlogA: id 7, name (CharField): "B4", info (CharField): "i4">,
<BlogA: id 6, name (CharField): "B3", info (CharField): "i3">, <BlogA: id 6, name (CharField): "B3", info (CharField): "i3">,
@ -181,8 +203,20 @@ class testclass(TestCase):
<BlogB: id 2, name (CharField): "Bb1">, <BlogB: id 2, name (CharField): "Bb1">,
<BlogB: id 3, name (CharField): "Bb2">, <BlogB: id 3, name (CharField): "Bb2">,
<BlogB: id 4, name (CharField): "Bb3"> ]''' <BlogB: id 4, name (CharField): "Bb3"> ]'''
# PostgreSQL returns this order
expected2='''
[ <BlogB: id 2, name (CharField): "Bb1">,
<BlogB: id 3, name (CharField): "Bb2">,
<BlogB: id 4, name (CharField): "Bb3">,
<BlogA: id 8, name (CharField): "B5", info (CharField): "i5">,
<BlogA: id 7, name (CharField): "B4", info (CharField): "i4">,
<BlogA: id 6, name (CharField): "B3", info (CharField): "i3">,
<BlogA: id 5, name (CharField): "B2", info (CharField): "i2">,
<BlogA: id 1, name (CharField): "B1", info (CharField): "i1"> ]'''
x = '\n' + repr(BlogBase.objects.order_by('-BlogA___info')) x = '\n' + repr(BlogBase.objects.order_by('-BlogA___info'))
assert x == expected assert x == expected1 or x == expected2
#assert False #assert False
@ -196,6 +230,7 @@ __test__ = {"doctest": """
>>> get_version() >>> get_version()
'0.5 beta' '0.5 beta'
### simple inheritance ### simple inheritance
>>> o=Model2A.objects.create(field1='A1') >>> o=Model2A.objects.create(field1='A1')
@ -212,6 +247,32 @@ __test__ = {"doctest": """
>>> o.get_real_instance() >>> o.get_real_instance()
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> <Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>
### ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent, also with annotate()
>>> o=ModelShow1.objects.create(field1='abc')
>>> o.m2m.add(o) ; o.save()
>>> ModelShow1.objects.all()
[ <ModelShow1: id 1, field1 (CharField), m2m (ManyToManyField)> ]
>>> o=ModelShow2.objects.create(field1='abc')
>>> o.m2m.add(o) ; o.save()
>>> ModelShow2.objects.all()
[ <ModelShow2: id 1, field1: "abc", m2m: 1> ]
>>> o=ModelShow3.objects.create(field1='abc')
>>> o.m2m.add(o) ; o.save()
>>> ModelShow3.objects.all()
[ <ModelShow3: id 1, field1 (CharField): "abc", m2m (ManyToManyField): 1> ]
>>> ModelShow1.objects.all().annotate(Count('m2m'))
[ <ModelShow1: id 1, field1 (CharField), m2m (ManyToManyField) - Ann: m2m__count (int)> ]
>>> ModelShow2.objects.all().annotate(Count('m2m'))
[ <ModelShow2: id 1, field1: "abc", m2m: 1 - Ann: m2m__count: "1"> ]
>>> ModelShow3.objects.all().annotate(Count('m2m'))
[ <ModelShow3: id 1, field1 (CharField): "abc", m2m (ManyToManyField): 1 - Ann: m2m__count (int): "1"> ]
### extra() method ### extra() method
>>> Model2A.objects.extra(where=['id IN (2, 3)']) >>> Model2A.objects.extra(where=['id IN (2, 3)'])
@ -222,6 +283,7 @@ __test__ = {"doctest": """
[ <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)> ]
### class filtering, instance_of, not_instance_of ### class filtering, instance_of, not_instance_of
>>> Model2A.objects.instance_of(Model2B) >>> Model2A.objects.instance_of(Model2B)
@ -246,6 +308,7 @@ __test__ = {"doctest": """
[ <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)> ]
### get & delete ### get & delete
>>> oa=Model2A.objects.get(id=2) >>> oa=Model2A.objects.get(id=2)
@ -257,6 +320,7 @@ __test__ = {"doctest": """
[ <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)> ]
### queryset combining ### queryset combining
>>> o=ModelX.objects.create(field_x='x') >>> o=ModelX.objects.create(field_x='x')
@ -266,6 +330,7 @@ __test__ = {"doctest": """
[ <ModelX: id 1, field_b (CharField), field_x (CharField)>, [ <ModelX: id 1, field_b (CharField), field_x (CharField)>,
<ModelY: id 2, field_b (CharField), field_y (CharField)> ] <ModelY: id 2, field_b (CharField), field_y (CharField)> ]
### multiple inheritance, subclassing third party models (mix PolymorphicModel with models.Model) ### multiple inheritance, subclassing third party models (mix PolymorphicModel with models.Model)
>>> o = Enhance_Base.objects.create(field_b='b-base') >>> o = Enhance_Base.objects.create(field_b='b-base')
@ -275,6 +340,7 @@ __test__ = {"doctest": """
[ <Enhance_Base: id 1, field_b (CharField): "b-base">, [ <Enhance_Base: id 1, field_b (CharField): "b-base">,
<Enhance_Inherit: id 2, field_b (CharField): "b-inherit", field_p (CharField): "p", field_i (CharField): "i"> ] <Enhance_Inherit: id 2, field_b (CharField): "b-inherit", field_p (CharField): "p", field_i (CharField): "i"> ]
### ForeignKey, ManyToManyField ### ForeignKey, ManyToManyField
>>> obase=RelationBase.objects.create(field_base='base') >>> obase=RelationBase.objects.create(field_base='base')
@ -284,27 +350,27 @@ __test__ = {"doctest": """
>>> oa.m2m.add(oa); oa.m2m.add(ob) >>> oa.m2m.add(oa); oa.m2m.add(ob)
>>> RelationBase.objects.all() >>> RelationBase.objects.all()
[ <RelationBase: id 1, field_base (CharField): "base", fk (ForeignKey): "None">, [ <RelationBase: id 1, field_base (CharField): "base", fk (ForeignKey): "None", m2m (ManyToManyField): 0>,
<RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2">, <RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2", m2m (ManyToManyField): 2>,
<RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2">, <RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2", m2m (ManyToManyField): 1>,
<RelationBC: id 4, field_base (CharField): "C1", fk (ForeignKey): "RelationA", field_b (CharField): "C2", field_c (CharField): "C3"> ] <RelationBC: id 4, field_base (CharField): "C1", fk (ForeignKey): "RelationA", field_b (CharField): "C2", field_c (CharField): "C3", m2m (ManyToManyField): 0> ]
>>> oa=RelationBase.objects.get(id=2) >>> oa=RelationBase.objects.get(id=2)
>>> oa.fk >>> oa.fk
<RelationBase: id 1, field_base (CharField): "base", fk (ForeignKey): "None"> <RelationBase: id 1, field_base (CharField): "base", fk (ForeignKey): "None", m2m (ManyToManyField): 0>
>>> oa.relationbase_set.all() >>> oa.relationbase_set.all()
[ <RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2">, [ <RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2", m2m (ManyToManyField): 1>,
<RelationBC: id 4, field_base (CharField): "C1", fk (ForeignKey): "RelationA", field_b (CharField): "C2", field_c (CharField): "C3"> ] <RelationBC: id 4, field_base (CharField): "C1", fk (ForeignKey): "RelationA", field_b (CharField): "C2", field_c (CharField): "C3", m2m (ManyToManyField): 0> ]
>>> ob=RelationBase.objects.get(id=3) >>> ob=RelationBase.objects.get(id=3)
>>> ob.fk >>> ob.fk
<RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2"> <RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2", m2m (ManyToManyField): 2>
>>> oa=RelationA.objects.get() >>> oa=RelationA.objects.get()
>>> oa.m2m.all() >>> oa.m2m.all()
[ <RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2">, [ <RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2", m2m (ManyToManyField): 2>,
<RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2"> ] <RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2", m2m (ManyToManyField): 1> ]
### user-defined manager ### user-defined manager
@ -320,6 +386,7 @@ __test__ = {"doctest": """
>>> type(ModelWithMyManager._default_manager) >>> type(ModelWithMyManager._default_manager)
<class 'polymorphic.tests.MyManager'> <class 'polymorphic.tests.MyManager'>
### Manager Inheritance ### Manager Inheritance
>>> type(MRODerived.objects) # MRO >>> type(MRODerived.objects) # MRO
@ -333,10 +400,12 @@ __test__ = {"doctest": """
>>> type(MROBase2._default_manager) >>> type(MROBase2._default_manager)
<class 'polymorphic.tests.MyManager'> <class 'polymorphic.tests.MyManager'>
### fixed issue in PolymorphicModel.__getattribute__: field name same as model name ### fixed issue in PolymorphicModel.__getattribute__: field name same as model name
>>> ModelFieldNameTest.objects.create(modelfieldnametest='1') >>> ModelFieldNameTest.objects.create(modelfieldnametest='1')
<ModelFieldNameTest: id 1, modelfieldnametest (CharField)> <ModelFieldNameTest: id 1, modelfieldnametest (CharField)>
### fixed issue in PolymorphicModel.__getattribute__: ### fixed issue in PolymorphicModel.__getattribute__:
# if subclass defined __init__ and accessed class members, __getattribute__ had a problem: "...has no attribute 'sub_and_superclass_dict'" # if subclass defined __init__ and accessed class members, __getattribute__ had a problem: "...has no attribute 'sub_and_superclass_dict'"
#>>> o #>>> o
@ -344,6 +413,7 @@ __test__ = {"doctest": """
>>> o.bar >>> o.bar
'XYZ' 'XYZ'
### Django model inheritance diamond problem, fails for Django 1.1 ### Django model inheritance diamond problem, fails for Django 1.1
#>>> o=DiamondXY.objects.create(field_b='b', field_x='x', field_y='y') #>>> o=DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')