- 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
pbackup
mcmd.py
dbconfig_local.py
pip-log.txt
build
ppreadme.py
ppdocs.py
common.css
screen.css
README.html
DOCS.html

View File

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

View File

@ -2,29 +2,24 @@
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)
class ArtProject(Project):
artist = models.CharField(max_length=30)
class ResearchProject(Project):
supervisor = models.CharField(max_length=30)
class ModelA(PolymorphicModel):
class ModelA(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelB(ModelA):
field2 = models.CharField(max_length=10)
class ModelC(ModelB):
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
# (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 manager import PolymorphicManager
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')

View File

@ -26,13 +26,13 @@ from django import VERSION as django_VERSION
from base import PolymorphicModelBase
from manager import PolymorphicManager
from query import PolymorphicQuerySet
from showfields import ShowFieldTypes
from showfields import ShowFieldType
###################################################################################
### PolymorphicModel
class PolymorphicModel(ShowFieldTypes, models.Model):
class PolymorphicModel(models.Model):
"""
Abstract base class that provides polymorphic behaviour
for any model directly or indirectly derived from it.
@ -53,7 +53,11 @@ class PolymorphicModel(ShowFieldTypes, models.Model):
"""
__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:
abstract = True
@ -130,10 +134,12 @@ class PolymorphicModel(ShowFieldTypes, models.Model):
name = model.__name__.lower()
if as_ptr: name+='_ptr'
result[name] = model
def add_all_base_models(model, result):
add_if_regular_sub_or_super_class(model, True, result)
for b in model.__bases__:
add_all_base_models(b, result)
def add_sub_models(model, result):
for b in model.__subclasses__():
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)
#print '---',self.__class__.__name__,name
return attr
return super(PolymorphicModel, self).__getattribute__(name)

View File

@ -144,6 +144,7 @@ class PolymorphicQuerySet(QuerySet):
for modelclass, idlist in idlist_per_model.items():
qs = modelclass.base_objects.filter(id__in=idlist)
qs.dup_select_related(self) # copy select related configuration to new qs
for o in qs:
if self.query.aggregates:
for anno in self.query.aggregates.keys():
@ -153,6 +154,13 @@ class PolymorphicQuerySet(QuerySet):
# re-create correct order and return result list
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
def iterator(self):
@ -182,7 +190,9 @@ class PolymorphicQuerySet(QuerySet):
reached_end = False
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:
reached_end = True
break
@ -194,8 +204,10 @@ class PolymorphicQuerySet(QuerySet):
if reached_end: raise StopIteration
def __repr__(self):
result = [ repr(o) for o in self.all() ]
return '[ ' + ',\n '.join(result) + ' ]'
def __repr__(self, *args, **kwargs):
if self.model.polymorphic_query_multiline_output:
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
def _represent_foreign_key(o):
if o is None:
out = '"None"'
else:
out = '"' + o.__class__.__name__ + '"'
return out
class ShowFieldsAndTypes(object):
""" model mixin, like ShowFields, but also show field types """
def __repr__(self):
out = '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__ + ')'
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 ShowFieldBase(object):
""" base class for the ShowField... model mixins, does the work """
polymorphic_query_multiline_output = True # cause nicer multiline PolymorphicQuery output
polymorphic_showfield_type = False
polymorphic_showfield_content = False
class ShowFields(object):
""" model mixin that shows the object's class, it's fields and field contents """
def __repr__(self):
out = 'id ' + str(self.pk) + ', '
for f in self._meta.fields:
return self.__unicode__()
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
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)
out += ': ' + _represent_foreign_key(o)
else:
out += ': "' + getattr(self, f.name) + '"'
return '<' + (self.__class__.__name__ + ': ') + out + '>'
class ShowFieldTypes(object):
""" INTERNAL; don't use this!
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 + '>'
if isinstance(f, (models.ForeignKey)):
out += ': ' + ( '"None"' if o is None else '"' + o.__class__.__name__ + '"' )
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.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.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):
field1 = models.CharField(max_length=10)
@ -20,14 +21,24 @@ class PlainB(PlainA):
class PlainC(PlainB):
field3 = models.CharField(max_length=10)
class Model2A(PolymorphicModel):
class Model2A(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10)
class Model2B(Model2A):
field2 = models.CharField(max_length=10)
class Model2C(Model2B):
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)
class ModelX(Base):
field_x = models.CharField(max_length=10)
@ -36,7 +47,7 @@ class ModelY(Base):
class Enhance_Plain(models.Model):
field_p = models.CharField(max_length=10)
class Enhance_Base(ShowFieldsAndTypes, PolymorphicModel):
class Enhance_Base(ShowFieldTypeAndContent, PolymorphicModel):
field_b = models.CharField(max_length=10)
class Enhance_Inherit(Enhance_Base, Enhance_Plain):
field_i = models.CharField(max_length=10)
@ -51,7 +62,7 @@ class DiamondY(DiamondBase):
class DiamondXY(DiamondX, DiamondY):
pass
class RelationBase(ShowFieldsAndTypes, PolymorphicModel):
class RelationBase(ShowFieldTypeAndContent, PolymorphicModel):
field_base = models.CharField(max_length=10)
fk = models.ForeignKey('self', null=True)
m2m = models.ManyToManyField('self')
@ -68,11 +79,11 @@ class RelatingModel(models.Model):
class MyManager(PolymorphicManager):
def get_query_set(self):
return super(MyManager, self).get_query_set().order_by('-field1')
class ModelWithMyManager(ShowFieldsAndTypes, Model2A):
class ModelWithMyManager(ShowFieldTypeAndContent, Model2A):
objects = MyManager()
field4 = models.CharField(max_length=10)
class MROBase1(PolymorphicModel):
class MROBase1(ShowFieldType, PolymorphicModel):
objects = MyManager()
field1 = models.CharField(max_length=10) # needed as MyManager uses it
class MROBase2(MROBase1):
@ -89,23 +100,23 @@ class MgrInheritA(models.Model):
class MgrInheritB(MgrInheritA):
mgrB = models.Manager()
field2 = models.CharField(max_length=10)
class MgrInheritC(ShowFieldsAndTypes, MgrInheritB):
class MgrInheritC(ShowFieldTypeAndContent, MgrInheritB):
pass
class BlogBase(ShowFieldsAndTypes, PolymorphicModel):
class BlogBase(ShowFieldTypeAndContent, PolymorphicModel):
name = models.CharField(max_length=10)
class BlogA(BlogBase):
info = models.CharField(max_length=10)
class BlogB(BlogBase):
pass
class BlogA_Entry(ShowFieldsAndTypes, PolymorphicModel):
class BlogEntry(ShowFieldTypeAndContent, PolymorphicModel):
blog = models.ForeignKey(BlogA)
text = models.CharField(max_length=10)
class ModelFieldNameTest(PolymorphicModel):
class ModelFieldNameTest(ShowFieldType, PolymorphicModel):
modelfieldnametest = models.CharField(max_length=10)
class InitTestModel(PolymorphicModel):
class InitTestModel(ShowFieldType, PolymorphicModel):
bar = models.CharField(max_length=100)
def __init__(self, *args, **kwargs):
kwargs['bar'] = self.x()
@ -115,13 +126,13 @@ class InitTestModelSubclass(InitTestModel):
return 'XYZ'
# test bad field name
#class TestBadFieldModel(PolymorphicModel):
#class TestBadFieldModel(ShowFieldType, PolymorphicModel):
# instance_of = models.CharField(max_length=10)
# validation error: "polymorphic.relatednameclash: Accessor for field 'polymorphic_ctype' clashes
# with related field 'ContentType.relatednameclash_set'." (reported by Andrew Ingram)
# fixed with related_name
class RelatedNameClash(PolymorphicModel):
class RelatedNameClash(ShowFieldType, PolymorphicModel):
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'
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')
entry1 = blog.bloga_entry_set.create(text='bla')
entry2 = BlogA_Entry.objects.create(blog=blog, text='bla2')
# create two blog entries in BlogA
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='Bb2')
o = BlogB.objects.create(name='Bb3')
qs = BlogBase.objects.annotate(entrycount=Count('BlogA___bloga_entry'))
assert qs[0].entrycount == 2
qs = BlogBase.objects.annotate(entrycount=Count('BlogA___blogentry'))
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
# 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='B5', info='i5')
# test ordering
### test ordering for field in all entries
expected = '''
[ <BlogB: id 4, name (CharField): "Bb3">,
<BlogB: id 3, name (CharField): "Bb2">,
@ -172,7 +191,10 @@ class testclass(TestCase):
x = '\n' + repr(BlogBase.objects.order_by('-name'))
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 7, name (CharField): "B4", info (CharField): "i4">,
<BlogA: id 6, name (CharField): "B3", info (CharField): "i3">,
@ -181,8 +203,20 @@ class testclass(TestCase):
<BlogB: id 2, name (CharField): "Bb1">,
<BlogB: id 3, name (CharField): "Bb2">,
<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'))
assert x == expected
assert x == expected1 or x == expected2
#assert False
@ -196,6 +230,7 @@ __test__ = {"doctest": """
>>> get_version()
'0.5 beta'
### simple inheritance
>>> o=Model2A.objects.create(field1='A1')
@ -212,6 +247,32 @@ __test__ = {"doctest": """
>>> o.get_real_instance()
<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
>>> Model2A.objects.extra(where=['id IN (2, 3)'])
@ -222,6 +283,7 @@ __test__ = {"doctest": """
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
### class filtering, instance_of, not_instance_of
>>> Model2A.objects.instance_of(Model2B)
@ -246,6 +308,7 @@ __test__ = {"doctest": """
[ <Model2B: id 2, field1 (CharField), field2 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
### get & delete
>>> oa=Model2A.objects.get(id=2)
@ -257,6 +320,7 @@ __test__ = {"doctest": """
[ <Model2A: id 1, field1 (CharField)>,
<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
### queryset combining
>>> o=ModelX.objects.create(field_x='x')
@ -266,6 +330,7 @@ __test__ = {"doctest": """
[ <ModelX: id 1, field_b (CharField), field_x (CharField)>,
<ModelY: id 2, field_b (CharField), field_y (CharField)> ]
### multiple inheritance, subclassing third party models (mix PolymorphicModel with models.Model)
>>> 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_Inherit: id 2, field_b (CharField): "b-inherit", field_p (CharField): "p", field_i (CharField): "i"> ]
### ForeignKey, ManyToManyField
>>> obase=RelationBase.objects.create(field_base='base')
@ -284,27 +350,27 @@ __test__ = {"doctest": """
>>> oa.m2m.add(oa); oa.m2m.add(ob)
>>> RelationBase.objects.all()
[ <RelationBase: id 1, field_base (CharField): "base", fk (ForeignKey): "None">,
<RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2">,
<RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2">,
<RelationBC: id 4, field_base (CharField): "C1", fk (ForeignKey): "RelationA", field_b (CharField): "C2", field_c (CharField): "C3"> ]
[ <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", m2m (ManyToManyField): 2>,
<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", m2m (ManyToManyField): 0> ]
>>> oa=RelationBase.objects.get(id=2)
>>> 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()
[ <RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2">,
<RelationBC: id 4, field_base (CharField): "C1", fk (ForeignKey): "RelationA", field_b (CharField): "C2", field_c (CharField): "C3"> ]
[ <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", m2m (ManyToManyField): 0> ]
>>> ob=RelationBase.objects.get(id=3)
>>> 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.m2m.all()
[ <RelationA: id 2, field_base (CharField): "A1", fk (ForeignKey): "RelationBase", field_a (CharField): "A2">,
<RelationB: id 3, field_base (CharField): "B1", fk (ForeignKey): "RelationA", field_b (CharField): "B2"> ]
[ <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", m2m (ManyToManyField): 1> ]
### user-defined manager
@ -320,6 +386,7 @@ __test__ = {"doctest": """
>>> type(ModelWithMyManager._default_manager)
<class 'polymorphic.tests.MyManager'>
### Manager Inheritance
>>> type(MRODerived.objects) # MRO
@ -333,10 +400,12 @@ __test__ = {"doctest": """
>>> type(MROBase2._default_manager)
<class 'polymorphic.tests.MyManager'>
### fixed issue in PolymorphicModel.__getattribute__: field name same as model name
>>> ModelFieldNameTest.objects.create(modelfieldnametest='1')
<ModelFieldNameTest: id 1, modelfieldnametest (CharField)>
### fixed issue in PolymorphicModel.__getattribute__:
# if subclass defined __init__ and accessed class members, __getattribute__ had a problem: "...has no attribute 'sub_and_superclass_dict'"
#>>> o
@ -344,6 +413,7 @@ __test__ = {"doctest": """
>>> o.bar
'XYZ'
### Django model inheritance diamond problem, fails for Django 1.1
#>>> o=DiamondXY.objects.create(field_b='b', field_x='x', field_y='y')