2015-11-26 34 views
0

我正在嘗試使用Persistent和Servant,所以我沒有將URL段自動解析爲Persistent鍵的好處。相反,我建立了我的路線以要求Int64,並且我想使用它來檢索記錄來執行主鍵查找。獲取持久性記錄給定其整數密鑰?

Everything I've foundpoints to using toSqlKey整數轉換爲關鍵,所以我試着寫一個很簡單的功能,將做到這一點對我來說:

runDB :: (MonadBaseControl IO m, MonadIO m) => (SqlPersistT (NoLoggingT (ResourceT m))) a -> m a 
runDB actions = do 
    filename <- liftIO $ getEnv "SQLITE_FILENAME" 
    runSqlite (pack filename) actions 

getRecordByKey :: Int64 -> IO (Maybe (Entity Record)) 
getRecordByKey recordId = runDB $ get (toSqlKey recordId) 

不幸的是,這並不工作;我有以下類型的錯誤:

Couldn't match expected type ‘PersistEntityBackend 
           (Entity Record)’ 
      with actual type ‘SqlBackend’ 
In the second argument of ‘($)’, namely ‘get (toSqlKey recordId)’ 
In the expression: runDB $ get (toSqlKey recordId) 
In an equation for ‘getRecordByKey’: 
    getRecordByKey recordId = runDB $ get (toSqlKey recordId) 

排序的理解這個錯誤,我擡頭的類型gettoSqlKey,它們包括相關的約束:

get :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val) => Key val -> ReaderT backend m (Maybe val) 
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record 

如果我沒有理解正確地說,backendPersistEntityBackend val需要是相同的類型,但toSqlKey強制執行SqlBackend約束,所以類型不匹配。我的直覺告訴我,PersistentEntityBackend (Entity Record)應該是SqlBackend,但顯然我錯了。不過,我不知道爲什麼或者如何。

無論如何,我不知道在分析中我是對還是錯,但無論如何,我不確定如何解決這個問題或者正確的方法。如何/我應該從我的數據庫獲得一個記錄給定一個整數?

+0

這是這些東西,是很難從遠處一個 - 但我不認爲你需要的'liftIO $回報row'在所有 - 只是'runDB $ GET(toSqlKey的recordId)'應該足夠 – Carsten

+0

@Carsten啊!關於第二點你說的很對 - 在我試圖調試問題之前,我已經做得更多了。至於代碼,我認爲這幾乎都是相關的,因爲它實際上並不依賴於我的模型的任何方面。讓我知道如果你需要更多的上下文,但我可以把它放到一個單獨的沙箱中,並且很容易地重現錯誤。 –

+0

你會介意嘗試一些東西(我需要一些時間才能將所有東西放入沙盒中):將'runDB'改爲'SqlPersistT IO a - > IO a',並從'recordByKey'中刪除類型簽名,因爲我真的確定這應該起作用,它最有可能是一些討厭的類型的東西在那裏與單變換器 – Carsten

回答

2

這個工作對我來說(可能取決於你的包的版本...黯然):

{-# LANGUAGE FlexibleContexts #-} 
module Stackoverlflow where 

import Control.Monad.IO.Class (MonadIO, liftIO) 
import Control.Monad.Logger(NoLoggingT) 
import Control.Monad.Trans.Control (MonadBaseControl) 
import Control.Monad.Trans.Resource (ResourceT) 
import Data.Int (Int64) 
import Data.Text (pack) 
import Database.Persist.Class (ToBackendKey, get) 
import Database.Persist.Sql (SqlBackend, SqlPersistT, toSqlKey) 
import Database.Persist.Sqlite(runSqlite) 
import Database.Persist.Types (Entity) 
import System.Environment (getEnv) 

runDB :: (MonadBaseControl IO m, MonadIO m) => 
     (SqlPersistT (NoLoggingT (ResourceT m))) a -> m a 
runDB actions = do 
    filename <- liftIO $ getEnv "SQLITE_FILENAME" 
    runSqlite (pack filename) actions 

getRecordByKey :: (MonadIO m, ToBackendKey SqlBackend val, MonadBaseControl IO m) => 
       Int64 -> m (Maybe val) 
getRecordByKey recordId = runDB $ get (toSqlKey recordId) 

,你可以看到我剛剛添加了很多類型的註釋(GHC好後沒有刪除我的簽名並要求它告訴我;))

也請注意,我沒有你的Record所以你應該很容易能夠擺脫... val的東西!

+0

啊,我明白了,我們對複雜的類型定義有相同的方法;) – SmokeDispenser

+0

那麼,這是一種方法......並且可能是更好的方法。因此,我認爲我發現原來的問題是:'get'不返回'Maybe(實體記錄)',它返回'Maybe Record'。畢竟,那*是問題所在。即使有了這些知識,錯誤信息仍然是不可理解的,但嘿,所以它就這樣了。 :P –

+0

哦,我現在看到它。問題是'PersistEntityBackend(實體記錄)'不存在,但'PersistEntityBackend Record'確實存在,它是'SqlBackend'。謝謝你的幫助! –

1

那麼直接製作Key record是否爲FromText/ToText的一個實例,並直接使用URL中的鍵?

{-# LANGUAGE FlexibleContexts    #-} 
{-# LANGUAGE UndecidableInstances    #-} 
instance ToBackendKey SqlBackend record => FromText (Key record) where 
    fromText k = toSqlKey <$> fromText k 
instance ToBackendKey SqlBackend record => ToText (Key record) where 
    toText = toText . fromSqlKey