From 52f32c1fc37a3b808854c8dd03accd8757a15f15 Mon Sep 17 00:00:00 2001 From: Bert Constantin Date: Sun, 31 Oct 2010 12:27:21 +0100 Subject: [PATCH] improved showfields.py for better and more precise output, updated tests --- pexp/management/commands/p2cmd.py | 46 +++++++++ pexp/models.py | 10 ++ polymorphic/showfields.py | 144 +++++++++++++++++++++-------- polymorphic/tests.py | 149 +++++++++++++++++------------- 4 files changed, 247 insertions(+), 102 deletions(-) create mode 100644 pexp/management/commands/p2cmd.py diff --git a/pexp/management/commands/p2cmd.py b/pexp/management/commands/p2cmd.py new file mode 100644 index 0000000..6901eb3 --- /dev/null +++ b/pexp/management/commands/p2cmd.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" +This module is a scratchpad for general development, testing & debugging +""" +import uuid + +from django.core.management.base import NoArgsCommand +from django.db.models import connection +from pprint import pprint +import settings + +from pexp.models import * + +def reset_queries(): + connection.queries=[] + +def show_queries(): + print; print 'QUERIES:',len(connection.queries); pprint(connection.queries); print; connection.queries=[] + +class Command(NoArgsCommand): + help = "" + + def handle_noargs(self, **options): + print 'polycmd - sqlite test db is stored in:',settings.SQLITE_DB_PATH + print + + Project.objects.all().delete() + a=Project.objects.create(topic="John's gathering") + b=ArtProject.objects.create(topic="Sculpting with Tim", artist="T. Turner") + c=ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter") + print Project.objects.all() + print + + ModelA.objects.all().delete() + a=ModelA.objects.create(field1='A1') + b=ModelB.objects.create(field1='B1', field2='B2') + c=ModelC.objects.create(field1='C1', field2='C2', field3='C3') + print ModelA.objects.extra( select={"select1": "field1 = 'A1'", "select2": "field1 = 'A0'"} ) + print + + if not 'UUIDField' in globals(): return + UUIDModelA.objects.all().delete() + a=UUIDModelA.objects.create(field1='012345678900123456789001234567890012345678900123456789001234567890') + b=UUIDModelB.objects.create(field1='B1', field2='B2') + c=UUIDModelC.objects.create(field1='C1', field2='C2', field3='C3') + print UUIDModelA.objects.all() diff --git a/pexp/models.py b/pexp/models.py index be20b81..b6f2a83 100644 --- a/pexp/models.py +++ b/pexp/models.py @@ -39,3 +39,13 @@ if not (django_VERSION[0]<=1 and django_VERSION[1]<=1): class Model2C(Model2B): field3 = models.CharField(max_length=10) +try: from polymorphic.test_tools import UUIDField +except: pass +if 'UUIDField' in globals(): + class UUIDModelA(ShowFieldTypeAndContent, PolymorphicModel): + uuid_primary_key = UUIDField(primary_key = True) + field1 = models.CharField(max_length=10) + class UUIDModelB(UUIDModelA): + field2 = models.CharField(max_length=10) + class UUIDModelC(UUIDModelB): + field3 = models.CharField(max_length=10) diff --git a/polymorphic/showfields.py b/polymorphic/showfields.py index 0979018..cd7c9a1 100644 --- a/polymorphic/showfields.py +++ b/polymorphic/showfields.py @@ -1,64 +1,134 @@ # -*- coding: utf-8 -*- from django.db import models - +from pprint import pformat 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 + # these may be overridden by the user + polymorphic_showfield_max_line_width = None + polymorphic_showfield_max_field_width = 20 + polymorphic_showfield_old_format = False + def __repr__(self): 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: + def _showfields_get_content(self, field_name, field_type=type(None)): + "helper for __unicode__" + content = getattr(self, field_name) + if self.polymorphic_showfield_old_format: out = ': ' + else: out = ' ' + if issubclass(field_type, models.ForeignKey): + if content is None: out += 'None' + else: out += content.__class__.__name__ + elif issubclass(field_type, models.ManyToManyField): + out += '%d' % content.count() + elif type(content) in (int,long): + out += unicode(content) + elif content is None: + out += 'None' + else: + txt=unicode(content) + if len(txt)>self.polymorphic_showfield_max_field_width: + txt=txt[:self.polymorphic_showfield_max_field_width-2]+'..' + out += '"' + txt + '"' + return out - if f.name in [ 'id' ] + self.polymorphic_internal_model_fields or 'ptr' in f.name: continue - out += ', ' + f.name + def _showfields_add_regular_fields(self, parts): + "helper for __unicode__" + done_fields = set() + for field in self._meta.fields + self._meta.many_to_many: + if field.name in self.polymorphic_internal_model_fields or '_ptr' in field.name: continue + if field.name in done_fields: continue # work around django diamond inheritance problem + done_fields.add(field.name) + + out = field.name - if self.polymorphic_showfield_type: - out += ' (' + type(f).__name__ + ')' + # if this is the standard primary key named "id", print it as we did with older versions of django_polymorphic + if field.primary_key and field.name=='id' and type(field)==models.AutoField: + out += ' '+ unicode(getattr(self, field.name)) - if self.polymorphic_showfield_content: - o = getattr(self, f.name) - - if isinstance(f, (models.ForeignKey)): - #out += ': ' + ( '"None"' if o is None else '"' + o.__class__.__name__ + '"' ) - out += ': ' - if o is None: - out += '"None"' - else: - out += '"' + o.__class__.__name__ + '"' - - elif isinstance(f, (models.ManyToManyField)): - out += ': %d' % o.count() - - else: - out += ': "' + unicode(o) + '"' - - def get_dynamic_fields(field_list, title): - out = ' - '+title+': ' - for an in field_list: - if an != field_list[0]: - out += ', ' - out += an + # otherwise, display it just like all other fields (with correct type, shortened content etc.) + else: if self.polymorphic_showfield_type: - out += ' (' + type(getattr(self, an)).__name__ + ')' + out += ' (' + type(field).__name__ + if field.primary_key: out += '/pk' + out += ')' + if self.polymorphic_showfield_content: - out += ': "' + unicode(getattr(self, an)) + '"' - return out + out += self._showfields_get_content(field.name,type(field)) + parts.append((False, out,',')) + + def _showfields_add_dynamic_fields(self, field_list, title, parts): + "helper for __unicode__" + parts.append( ( True, '- '+title, ':' ) ) + for field_name in field_list: + out = field_name + content = getattr(self, field_name) + if self.polymorphic_showfield_type: + out += ' (' + type(content).__name__ + ')' + if self.polymorphic_showfield_content: + out += self._showfields_get_content(field_name) + + parts.append( ( False, out, ',' ) ) + + def __unicode__(self): + # create list ("parts") containing one tuple for each title/field: + # ( bool: new section , item-text , separator to use after item ) + + # start with model name + parts = [ (True, self.__class__.__name__, ':') ] + + # add all regular fields + self._showfields_add_regular_fields(parts) + + # add annotate fields if hasattr(self,'polymorphic_annotate_names'): - out+=get_dynamic_fields(self.polymorphic_annotate_names, 'Ann') + self._showfields_add_dynamic_fields(self.polymorphic_annotate_names, 'Ann', parts) + # add extra() select fields if hasattr(self,'polymorphic_extra_select_names'): - out+=get_dynamic_fields(self.polymorphic_extra_select_names, 'Extra') + self._showfields_add_dynamic_fields(self.polymorphic_extra_select_names, 'Extra', parts) + + # format result + + indent = len(self.__class__.__name__)+5 + indentstr = ''.rjust(indent) + out=u''; xpos=0; possible_line_break_pos = None + + for i in xrange(len(parts)): + new_section, p, separator = parts[i] + final = (i==len(parts)-1) + if not final: + next_new_section, _, _ = parts[i+1] + + if ( self.polymorphic_showfield_max_line_width + and xpos+len(p) > self.polymorphic_showfield_max_line_width + and possible_line_break_pos!=None ): + rest = out[possible_line_break_pos:] + out = out[:possible_line_break_pos] + out+= '\n'+indentstr+rest + xpos=indent+len(rest) + + out += p; xpos += len(p) + + if not final: + if not next_new_section: + out += separator; xpos += len(separator) + out += ' '; xpos += 1 + + if not new_section: + possible_line_break_pos=len(out) + + return u'<'+out+'>' - return out+'>' class ShowFieldType(ShowFieldBase): """ model mixin that shows the object's class and it's field types """ diff --git a/polymorphic/tests.py b/polymorphic/tests.py index fd27c05..b692cbb 100644 --- a/polymorphic/tests.py +++ b/polymorphic/tests.py @@ -5,13 +5,14 @@ import settings import sys +from pprint import pprint +from django import VERSION as django_VERSION from django.test import TestCase from django.db.models.query import QuerySet 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 from polymorphic import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent, get_version @@ -155,18 +156,29 @@ class InitTestModelSubclass(InitTestModel): def x(self): return 'XYZ' -try: from polymorphic.test_tools import UUIDField -except: pass -if 'UUIDField' in globals(): - import uuid - class UUIDProject(ShowFieldTypeAndContent, PolymorphicModel): - id = UUIDField(primary_key = True) - topic = models.CharField(max_length = 30) - class UUIDArtProject(UUIDProject): - artist = models.CharField(max_length = 30) - class UUIDResearchProject(UUIDProject): - supervisor = models.CharField(max_length = 30) +# UUID tests won't work with Django 1.1 +if not (django_VERSION[0] <= 1 and django_VERSION[1] <= 1): + try: from polymorphic.test_tools import UUIDField + except: pass + if 'UUIDField' in globals(): + import uuid + + class UUIDProject(ShowFieldTypeAndContent, PolymorphicModel): + uuid_primary_key = UUIDField(primary_key = True) + topic = models.CharField(max_length = 30) + class UUIDArtProject(UUIDProject): + artist = models.CharField(max_length = 30) + class UUIDResearchProject(UUIDProject): + supervisor = models.CharField(max_length = 30) + + class UUIDPlainA(models.Model): + uuid_primary_key = UUIDField(primary_key = True) + field1 = models.CharField(max_length=10) + class UUIDPlainB(UUIDPlainA): + field2 = models.CharField(max_length=10) + class UUIDPlainC(UUIDPlainB): + field3 = models.CharField(max_length=10) # test bad field name @@ -226,14 +238,14 @@ class testclass(TestCase): ### test ordering for field in all entries expected = ''' -[ , - , - , - , - , - , - , - ]''' +[ , + , + , + , + , + , + , + ]''' x = '\n' + repr(BlogBase.objects.order_by('-name')) assert x == expected @@ -241,25 +253,25 @@ class testclass(TestCase): # MySQL and SQLite return this order expected1=''' -[ , - , - , - , - , - , - , - ]''' +[ , + , + , + , + , + , + , + ]''' # PostgreSQL returns this order expected2=''' -[ , - , - , - , - , - , - , - ]''' +[ , + , + , + , + , + , + , + ]''' x = '\n' + repr(BlogBase.objects.order_by('-BlogA___info')) assert x == expected1 or x == expected2 @@ -287,17 +299,25 @@ class testclass(TestCase): b=qs[1] c=qs[2] assert len(qs)==3 - assert type(a.id)==uuid.UUID and type(a.pk)==uuid.UUID + assert type(a.uuid_primary_key)==uuid.UUID and type(a.pk)==uuid.UUID res=repr(qs) import re - res=re.sub(' id ...................................., topic',' id, topic',res) - res_exp="""[ , - , - ]""" - assert res==res_exp + res=re.sub(' "(.*?)..", topic',', topic',res) + res_exp="""[ , + , + ]""" + assert res==res_exp, res + #if (a.pk!= uuid.UUID or c.pk!= uuid.UUID): + # print + # print '# known inconstency with custom primary key field detected (django problem?)' + + a=UUIDPlainA.objects.create(field1='A1') + b=UUIDPlainB.objects.create(field1='B1', field2='B2') + c=UUIDPlainC.objects.create(field1='C1', field2='C2', field3='C3') + qs=UUIDPlainA.objects.all() if (a.pk!= uuid.UUID or c.pk!= uuid.UUID): print - print '# known django object inconstency with custom primary key field detected' + print '# known type inconstency with custom primary key field detected (django problem?)' def show_base_manager(model): @@ -401,19 +421,19 @@ __test__ = {"doctest": """ >>> o=ModelShow2.objects.create(field1='abc') >>> o.m2m.add(o) ; o.save() >>> ModelShow2.objects.all() -[ ] +[ ] >>> o=ModelShow3.objects.create(field1='abc') >>> o.m2m.add(o) ; o.save() >>> ModelShow3.objects.all() -[ ] +[ ] >>> ModelShow1.objects.all().annotate(Count('m2m')) [ ] >>> ModelShow2.objects.all().annotate(Count('m2m')) -[ ] +[ ] >>> ModelShow3.objects.all().annotate(Count('m2m')) -[ ] +[ ] # no pretty printing >>> o=ModelShow1_plain.objects.create(field1='abc') @@ -439,10 +459,9 @@ __test__ = {"doctest": """ >>> o=ModelExtraExternal.objects.create(topic='extra2') >>> o=ModelExtraExternal.objects.create(topic='extra3') >>> ModelExtraA.objects.extra(tables=["polymorphic_modelextraexternal"], select={"topic":"polymorphic_modelextraexternal.topic"}, where=["polymorphic_modelextraa.id = polymorphic_modelextraexternal.id"] ) -[ , - , - ] - +[ , + , + ] ### class filtering, instance_of, not_instance_of @@ -501,8 +520,8 @@ __test__ = {"doctest": """ >>> o = Enhance_Inherit.objects.create(field_b='b-inherit', field_p='p', field_i='i') >>> Enhance_Base.objects.all() -[ , - ] +[ , + ] ### ForeignKey, ManyToManyField @@ -514,27 +533,27 @@ __test__ = {"doctest": """ >>> oa.m2m.add(oa); oa.m2m.add(ob) >>> RelationBase.objects.all() -[ , - , - , - ] +[ , + , + , + ] >>> oa=RelationBase.objects.get(id=2) >>> oa.fk - + >>> oa.relationbase_set.all() -[ , - ] +[ , + ] >>> ob=RelationBase.objects.get(id=3) >>> ob.fk - + >>> oa=RelationA.objects.get() >>> oa.m2m.all() -[ , - ] +[ , + ] ### user-defined manager @@ -542,8 +561,8 @@ __test__ = {"doctest": """ >>> o=ModelWithMyManager.objects.create(field1='D1b', field4='D4b') >>> ModelWithMyManager.objects.all() -[ , - ] +[ , + ] >>> type(ModelWithMyManager.objects)