An ORM for Common Lisp. (forked from

Fernando Borretti 1a85295d7e Merge pull request #50 from PuercoPop/patch-2 8 years ago
docs 1a9e836160 Fix table docs 8 years ago
src dd2f48bdbe deftable: use &body for slots-and-options 8 years ago
t 28537fe259 Add more tests 9 years ago
.gitignore 5484891f77 Update .gitignore 9 years ago
.travis.yml 8ce46a26d6 Clone sxql and cl-dbi 8 years ago a1bd7449c1 Add QL badge 8 years ago
crane-test.asd 28537fe259 Add more tests 9 years ago
crane.asd 28537fe259 Add more tests 9 years ago


Build Status Quicklisp

Crane is an ORM for Common Lisp, providing a simple bridge between CLOS and relational databases, and out of the box migrations.


Defining Tables

(deftable user ()
  (name :type text :uniquep t)
  (age :type integer :nullp nil :initform 18)
  (friend :type integer :foreign user)))

The foreign argument accepts a symbol that represents another table or a sexp of the form (table &key on-delete on-update)), where acceptable values are :no-action :restrict :cascade :set-null :set-default.


(deftable user ()
  (name :type text :uniquep t :nullp nil)
  (age :type integer :nullp t :initform 18)
  (description :type text))

Just make the changes, and Crane will compute the diffs and perform all the ALTER TABLEs for you.


 (asdf:system-relative-pathname :myapp #p"migrations/")
   (:type :postgres
    :name "myapp_db"
    :user "user"
    :pass "user")))


For configuration management and switching databases in development/production environments, you might want to use Envy.

Creating, Saving, and Deleting Objects

(let ((instance (create 'ship :name "Dalliance"
                              :tonnage 77)))
  ;; FIXME: It's back luck to rename a ship
  (setf (name instance) "Serenity")
  ;; Expand the cargo hold
  (incf (tonnage instance) 25)
  ;; Save these changes!
  (save instance)
  ;; Time to retire
  (del instance))


(filter 'user) ;; Returns everything

(filter 'user :name "Eudoxia")

(filter 'user (:> :age 21))

;; Returns a single object
(single 'user :name "Eudoxia")

;; Throws an error if this returns more
;; than one object
(single! 'user (:< age 35))

;; t if a match exists, nil otherwise
(exists 'user :name "Eudoxia")

;; If this record doesn't exist create it
(get-or-create 'user :name "Eudoxia" :age 19)


;;;; Automatic
(with-transaction ()
  (let ((restaurants (filter 'restaurant ...)))
    (loop for restaurant in restaurants do
          (save restaurant))))

;;;; Manual
  (let ((restaurants (filter 'restaurant ...)))
    (loop for restaurant in restaurants do
          (save restaurant)))


;;;; initial-data.lisp
  (:name "eudoxia"
   :groups (:admin :staff))
  (:name "joe"
   :groups (:admin)))
  (:name "Initech"
   :city "Denver"))

;;;; myapp.asd
(asdf:defsystem myapp
  :defsystem-depends-on (:clos-fixtures)
  :components ((:module "src"
                ((:fixture "initial-data")))))


(definflate (stamp 'timestamp)
  ;; Inflate a timestamp value
  ;; into a timestamp object
  (local-time:universal-to-timestamp stamp))

(defdeflate (stamp local-time:timestamp)
  ;; Deflate a timestamp object
  ;; into a string
  (local-time:format-timestring nil stamp))


I'm in the process of moving the documentation to Codex, so for now you can check it out in the website.


Copyright (c) 2013 Fernando Borretti (

Released under the MIT license.