diff --git a/include/class.orm.php b/include/class.orm.php
index 45b300f64ee99bd3eeae9d50c556929f5d6e547b..50b0a3046448ce95b1d5a3842ee0a7ca40727c42 100644
--- a/include/class.orm.php
+++ b/include/class.orm.php
@@ -697,6 +697,10 @@ class VerySimpleModel {
             $pk[$f] = $this->ht[$f];
         return $pk;
     }
+
+    function __getDbFields() {
+        return $this->ht;
+    }
 }
 
 /**
@@ -707,40 +711,100 @@ class VerySimpleModel {
  * will delegate most all of the heavy lifting to the wrapped Model instance.
  */
 class AnnotatedModel {
+    static function wrap(VerySimpleModel $model, $extras=array(), $class=false) {
+        static $classes;
 
-    var $model;
-    var $annotations;
+        $class = $class ?: get_class($model);
 
-    function __construct($model, $annotations) {
-        $this->model = $model;
-        $this->annotations = $annotations;
-    }
+        if ($extras instanceof VerySimpleModel) {
+            $extra = "Writeable";
+        }
+        if (!isset($classes[$class])) {
+            $classes[$class] = eval(<<<END_CLASS
+class AnnotatedModel___{$class}_{$extra}
+extends {$class} {
+    var \$__overlay__;
+    use {$extra}AnnotatedModelTrait;
 
-    function __get($what) {
-        return $this->get($what);
+    function __construct(\$ht, \$annotations) {
+        parent::__construct(\$ht);
+        \$this->__overlay__ = \$annotations;
     }
-    function get($what) {
-        if (isset($this->annotations[$what]))
-            return $this->annotations[$what];
-        return $this->model->get($what, null);
+}
+return "AnnotatedModel___{$class}_{$extra}";
+END_CLASS
+            );
+        }
+        return new $classes[$class]($model->ht, $extras);
     }
+}
 
-    function __set($what, $to) {
-        return $this->set($what, $to);
+trait AnnotatedModelTrait {
+    function get($what) {
+        if (isset($this->__overlay__[$what]))
+            return $this->__overlay__[$what];
+        return parent::get($what);
     }
+
     function set($what, $to) {
-        if (isset($this->annotations[$what]))
+        if (isset($this->__overlay__[$what]))
             throw new OrmException('Annotated fields are read-only');
-        return $this->model->set($what, $to);
+        return parent::set($what, $to);
     }
 
     function __isset($what) {
-        return isset($this->annotations[$what]) || $this->model->__isset($what);
+        if (isset($this->__overlay__[$what]))
+            return true;
+        return parent::__isset($what);
     }
 
-    // Delegate everything else to the model
-    function __call($what, $how) {
-        return call_user_func_array(array($this->model, $what), $how);
+    function __getDbFields() {
+        return $this->__overlay__ + parent::__getDbFields();
+    }
+}
+
+/**
+ * Slight variant on the AnnotatedModelTrait, except that the overlay is
+ * another model. Its fields are preferred over the wrapped model's fields.
+ * Updates to the overlayed fields are tracked in the overlay model and
+ * therefore kept separate from the annotated model's fields. ::save() will
+ * call save on both models. Delete will only delete the overlay model (that
+ * is, the annotated model will remain).
+ */
+trait WriteableAnnotatedModelTrait {
+    function get($what) {
+        if ($this->__overlay__->__isset($what))
+            return $this->__overlay__->get($what);
+        return parent::get($what);
+    }
+
+    function set($what, $to) {
+        if ($this->__overlay__->__isset($what)) {
+            return $this->__overlay__->set($what, $to);
+        }
+        return parent::set($what, $to);
+    }
+
+    function __isset($what) {
+        if ($this->__overlay__->__isset($what))
+            return true;
+        return parent::__isset($what);
+    }
+
+    function __getDbFields() {
+        return $this->__overlay__->__getDbFields() + parent::__getDbFields();
+    }
+
+    function save() {
+        $this->__overlay__->save();
+        return parent::save();
+    }
+
+    function delete() {
+        if ($rv = $this->__overlay__->delete())
+            // Mark the annotated object as deleted
+            $this->__deleted__ = true;
+        return $rv;
     }
 }
 
@@ -1740,7 +1804,7 @@ implements IteratorAggregate {
         }
         // Wrap annotations in an AnnotatedModel
         if ($extras) {
-            $m = new AnnotatedModel($m, $extras);
+            $m = AnnotatedModel::wrap($m, $extras, $modelClass);
         }
         // TODO: If the model has deferred fields which are in $fields,
         // those can be resolved here
@@ -1933,12 +1997,14 @@ extends ModelResultSet {
     }
 
     function add($object, $at=false) {
-        if (!$object || !$object instanceof $this->model)
+        // NOTE: Attempting to compare $object to $this->model will likely
+        // be problematic, and limits creative applications of the ORM
+        if (!$object) {
             throw new Exception(sprintf(
-                'Attempting to add invalid object to list. Expected <%s>, but got <%s>',
-                $this->model,
-                get_class($object)
+                'Attempting to add invalid object to list. Expected <%s>, but got <NULL>',
+                $this->model
             ));
+        }
 
         foreach ($this->key as $field=>$value)
             $object->set($field, $value);