Skip to content

Commit

Permalink
[metabase#3] Better support for objects that don't have a primary cal…
Browse files Browse the repository at this point in the history
…led :id

TODO:- Documentation
     - Look at styleguide
  • Loading branch information
deanmarc25 committed Sep 12, 2017
1 parent 7a6f4f9 commit 8d9b6dd
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 6 deletions.
18 changes: 14 additions & 4 deletions src/toucan/models.clj
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,12 @@
(add-property! :timestamped?
:insert (fn [obj] (assoc obj :created-at (new-timestamp), :updated-at (new-timestamp)))
:update (fn [obj] (assoc obj :updated-at (new-timestamp))))"))
:update (fn [obj] (assoc obj :updated-at (new-timestamp))))")

(primary-key [this]
"Returns a keyword that represents the primary key.
The default implementation returns `:id`"))


;;; INTERNAL IMPL
Expand Down Expand Up @@ -337,19 +342,24 @@
:pre-update identity
:post-select identity
:pre-delete (constantly nil)
:hydration-keys (constantly nil)})
:hydration-keys (constantly nil)
:primary-key (constantly :id)})

(defn- invoke-model
"Fetch an object with a specific ID or all objects of type ENTITY from the DB.
(invoke-model Database) -> seq of all databases
(invoke-model Database 1) -> Database w/ ID 1
(invoke-model Database 1) -> Database w/ ID 1 (or primary-key)
(invoke-model Database :id 1 ...) -> A single Database matching some key-value args"
([model]
((resolve 'toucan.db/select) model))
([model id]
(when id
(invoke-model model :id id)))
(if (map? id)
(let [args (vec (flatten (into [] id)))
invoke-part (partial invoke-model model)]
(apply invoke-part args))
(invoke-model model (primary-key model) id))))
([model k v & more]
(apply (resolve 'toucan.db/select-one) model k v more)))

Expand Down
11 changes: 11 additions & 0 deletions test/toucan/models_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[models :as models])
[toucan.test-setup :as test]
(toucan.test-models [category :refer [Category], :as category]
[inventory :refer [Inventory map->InventoryInstance]]
[user :refer [User]]
[venue :refer [Venue map->VenueInstance]])
[toucan.util.test :as tu]))
Expand Down Expand Up @@ -177,3 +178,13 @@
(expect
#toucan.test_models.venue.VenueInstance{}
(empty (Venue :name "BevMo")))

;; Test using a primary key other than `id`
(expect
#toucan.test_models.inventory.InventoryInstance{:name "Computer", :sku "cp-1" :id 1}
(Inventory "cp-1"))

;; Test using multiple keys as a fake primary
(expect
#toucan.test_models.inventory.InventoryInstance{:name "Computer", :sku "cp-1" :id 1}
(Inventory {:sku "cp-1" :name "Computer"}))
9 changes: 9 additions & 0 deletions test/toucan/test_models/inventory.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(ns toucan.test-models.inventory
"A model with `:primary-key` set to `sku`"
(:require [toucan.models :as models]))

(models/defmodel Inventory :inventories)

(extend (class Inventory)
models/IModel
(merge models/IModelDefaults {:primary-key (constantly :sku)}))
16 changes: 14 additions & 2 deletions test/toucan/test_setup.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
(toucan [db :as db]
[models :as models])
(toucan.test-models [category :refer [Category]]
[inventory :refer [Inventory]]
[user :refer [User]]
[venue :refer [Venue]])
[toucan.util.test :as u])
Expand Down Expand Up @@ -52,7 +53,13 @@
name VARCHAR(256) UNIQUE NOT NULL,
\"parent-category-id\" INTEGER
);"
"TRUNCATE TABLE categories RESTART IDENTITY CASCADE;"))
"TRUNCATE TABLE categories RESTART IDENTITY CASCADE;"
;; Inventory
"CREATE TABLE IF NOT EXISTS inventories (
id SERIAL PRIMARY KEY,
sku VARCHAR(256) UNIQUE NOT NULL,
name VARCHAR(256) NOT NULL);"
"TRUNCATE TABLE inventories RESTART IDENTITY CASCADE;"))

(def ^java.sql.Timestamp jan-first-2017 (Timestamp/valueOf "2017-01-01 00:00:00"))

Expand All @@ -72,7 +79,12 @@
[{:name "bar"}
{:name "dive-bar", :parent-category-id 1}
{:name "resturaunt"}
{:name "mexican-resturaunt", :parent-category-id 3}]))
{:name "mexican-resturaunt", :parent-category-id 3}])
;; Inventory
(db/simple-insert-many! Inventory
[{:name "Computer" :sku "cp-1"}
{:name "Text Book" :sku "tb-1"}
{:name "Pencils" :sku "pn-1"}]))

(defn reset-db!
"Reset the DB to its initial state, creating tables if needed and inserting the initial test data."
Expand Down

0 comments on commit 8d9b6dd

Please sign in to comment.