给定完整密钥获取持久记录?

Fetch a Persistent record given its integral key?

我正在尝试将 Persistent 与 Servant 一起使用,因此我无法将 URL 段自动解析为 Persistent 键。相反,我已经将我的路由设置为需要 Int64,并且我想使用它来检索记录以执行主键查找。

Everything I've found 将整数转换为键,所以我尝试编写一个非常简单的函数来为我完成此操作:

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,但显然我错了。不过,我不知道为什么或如何。

无论如何,我不知道我的分析是对还是错,但无论哪种方式,我都不确定如何解决这个问题或者这样做的正确方法是什么。 can/should 我如何从我的数据库中获取一个整数的记录?

这对我有用(可能取决于你的包的版本......遗憾的是):

{-# 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 的东西!

那么如何使 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