n1 won't work because it is writable and therefore appears in the form, which means even if you leave it blank, an empty string is submitted as its value, which SQLFORM converts to a 0 (the default value is inserted only if no value is submitted).
Here are two options:
db.define_table('mytable',
Field('modifiable_number', 'integer', requires=IS_NOT_EMPTY()),
Field('original1', 'integer', readable=False, writable=False,
default=request.post_vars.modifiable_number),
Field('original2', 'integer'
, readable=False,
writable=False,
compute=lambda r: r.modifiable_number)
)
db.mytable._before_update = [lambda t, f: setattr(db.mytable.original2, 'compute', None)]
Because original1 is not writable, it will not appear in the form, which means no value will be submitted for it and therefore its default value will be inserted. Note, however, that this will not work in appadmin, which ignores the "writable" attribute and will therefore include original1 in the insert form.
original2 will use the computed value only on the initial insert. The _before_update callback removes the "compute" attribute before any updates, so no new value will be computed on updates. Note, if you need to do an insert
after an update during the
same HTTP request (which is probably not a common scenario), you would also need to add an _after_update callback to restore the "compute" attribute after the update.
Anthony