给定完整的密钥,获取持久记录?
Posted
技术标签:
【中文标题】给定完整的密钥,获取持久记录?【英文标题】:Fetch a Persistent record given its integral key? 【发布时间】:2015-11-26 08:08:04 【问题描述】:我正在尝试将 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)
我有点理解这个错误 - 我查找了 get
和 toSqlKey
的类型,它们包括相关的约束:
get :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val) => Key val -> ReaderT backend m (Maybe val)
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record
如果我理解正确,backend
和 PersistEntityBackend val
需要是同一类型,但 toSqlKey
强制执行 SqlBackend
约束,因此类型不匹配。我的直觉告诉我PersistentEntityBackend (Entity Record)
应该是 SqlBackend
,但显然我错了。不过,我不知道为什么或如何。
无论如何,我不知道我在该分析中是对还是错,但无论哪种方式,我都不确定如何解决这个问题或正确的做法是什么。给定一个整数,我如何/应该从我的数据库中获取记录?
【问题讨论】:
这是从远处看很难的事情之一 - 但我认为你根本不需要liftIO $ return row
- 只需 runDB $ get (toSqlKey recordId)
就足够了
@Carsten 啊!关于第二点,您说得很对——在我将其剥离以尝试调试问题之前,我做了更多工作。至于代码,我认为这几乎是所有相关的东西,因为它实际上并不依赖于我模型的任何方面。如果您需要更多上下文,请告诉我,但我能够将其放入一个孤立的沙箱中并非常简单地重现错误。
您介意尝试一下吗(我需要一些时间才能将所有内容都放入沙箱):将 runDB
更改为 SqlPersistT IO a -> IO a
并从 recordByKey
中删除类型签名,因为我是真的很确定这应该可以工作,而且很可能是 monad-transformers 中的一些讨厌的类型的东西
你有没有从Database.Persist.Base
尝试toPersistKey
?
@JanGreve Database.Persist.Base
似乎不存在于任何最新版本的persist...?
【参考方案1】:
这对我有用(可能取决于你的包的版本......遗憾的是):
-# 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
的东西!
【讨论】:
啊,我明白了,我们对复杂类型定义有相同的方法;) 嗯,这是一种方法……而且可能是更好的方法。尽管如此,我想我发现了最初的问题是什么:get
不返回Maybe (Entity Record)
,它返回Maybe Record
。毕竟,that 才是问题所在。即使有了这些知识,错误消息仍然难以理解,但是,嘿,就这样吧。 :P
哦,我现在明白了。问题是PersistEntityBackend (Entity Record)
不存在,但PersistEntityBackend Record
存在,它是SqlBackend
。感谢您的帮助!
没问题:一直发生在我身上——不要评判我,但要坚持不懈。我让 ghc-mod/emacs 写签名(比我聪明多了^^)【参考方案2】:
那么让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
【讨论】:
以上是关于给定完整的密钥,获取持久记录?的主要内容,如果未能解决你的问题,请参考以下文章