# 20120713¶

## New application attribute lino.Lino.override_modlib_models¶

Following a sudden inspiration, I added the new application attribute lino.Lino.override_modlib_models and adapted the applications under lino.apps. This is finally a solution to the old problem that all Lino applications which use lino.modlib.contacts were obliged to define themselves a non-abstract Person and a Company model. This problem was a perpetual stumbling block when writing tutorials and minimal applications.

## Fixed a bug in lino.utils.dumpy¶

Because of the above change I also made a double dump test, which revealed the following problem:

WARNING Abandoning with 6 unsaved instances from t:\data\luc\lino_local\dsbe\fixtures\d20120713.py:
- debts.Entry [u"Cannot set both 'Distribute' and 'Monthly rate'"] (6 object(s) with primary key 135, 136, 137, 140, 141
, 142)


This problem existed already before my sudden inspiration, and it would have caused me much more work if I had discovered it only after the 1.4.4 release.

Here is the application code (from lino.modlib.debts.models.Entry) which caused it:

def full_clean(self,*args,**kw):
if self.periods <= 0:
raise ValidationError(_("Periods must be > 0"))
if self.monthly_rate and self.distribute:
raise ValidationError(
_("Cannot set both 'Distribute' and 'Monthly rate'"))
super(Entry,self).full_clean(*args,**kw)


Changed this code so that the message becomes more specific:

WARNING Abandoning with 6 unsaved instances from t:\data\luc\lino_local\dsbe\fixtures\d20120713.py:
- debts.Entry [u"Cannot set 'Distribute' when 'Monthly rate' is '0'"] (6 object(s) with primary key 135, 136, 137, 140,
141, 142)


The generated dumpy file is as follows:

def create_debts_entry(id, seqno, ..., monthly_rate):
return debts_Entry(id=id,seqno=seqno,...,monthly_rate=monthly_rate)

def debts_entry_objects():
...
yield create_debts_entry(135,48,...,'0')


This is the reason for our problem. A DecimalField accepts a string value and will convert it to a Decimal, but only when reading it from the database. For example (supposing that Company.hourly_rate is a DecimalField):

>>> from lino.apps.pcsw.models import Company
>>> c = Company(name="test",hourly_rate='0.25')
>>> print repr(c.hourly_rate)
'0.25'
>>> c.save()
>>> print repr(c.hourly_rate)
'0.25'


>>> c2 = Company.objects.get(pk=c.pk)
>>> print repr(c2.hourly_rate)
Decimal('0.25')


It is one of Django’s oddnesses to allow storing a string value in a DecimalField and leave it there unconverted. We’ll forgive that oddness by modifying lino.utils.dumpy so that it generates code which instantiates models using true Decimal values:

def create_debts_entry(id, seqno, ... monthly_rate):
if monthly_rate is not None: monthly_rate = Decimal(monthly_rate)
return debts_Entry(id=id,seqno=seqno,...,monthly_rate=monthly_rate)


Note that another (easier) solution would have been to modify lino.utils.dumpy.Serializer.value2string() in order to write the correct value directly for each debts_entry_objects line instead of testing and converting each field in create_debts_entry:

def debts_entry_objects():
...
yield create_debts_entry(135,48,...,Decimal('0'))


But we prefer to keep the file size small (and load performance is less important).