如何在 Yesod / Persistent 中正确使用 runDB
Posted
技术标签:
【中文标题】如何在 Yesod / Persistent 中正确使用 runDB【英文标题】:How to use runDB correctly in Yesod / Persistent 【发布时间】:2014-01-02 20:10:38 【问题描述】:我有以下持久模型:
User
ident Text
password Text Maybe
UniqueUser ident
deriving Typeable
Payment
timestamp UTCTime
from UserId
to UserId
amount Int
我正在尝试构建一个页面,该页面显示数据库中所有付款的列表,每个付款都链接到付款/收到付款的用户。目前我的代码是:
getPaymentsR :: Handler html
getPaymentsR = do
let payments = map addFromTo $ runDB $ selectList [] [Desc PaymentTimestamp]
defaultLayout
[whamlet|
<h3> Payments
<ul>
$forall (key, value, from, to) <- payments
<li>
<a href=@UserR $ paymentFrom value> #from </a> paid #paymentAmount value to <a href=@UserR $ paymentTo value> #to </a> on #printDay $ paymentTimestamp value
$if null payments
<ul>
<li> No payments
|]
where
addFromTo :: Entity Payment -> (Key Payment, Payment, Text, Text)
addFromTo (Entity key val) = do
let from = runDB $ get404 $ paymentFrom val
let to = runDB $ get404 $ paymentTo val
(key, val, userIdent from, userIdent to)
但是我收到以下错误:
Handler/Payment.hs:9:36:
Couldn't match expected type `[Entity Payment]'
with actual type `HandlerT site0 IO [Entity Payment]'
In the second argument of `($)', namely
`runDB $ selectList [] [Desc PaymentTimestamp]'
In the expression:
map addFromTo $ runDB $ selectList [] [Desc PaymentTimestamp]
In an equation for `payments':
payments
= map addFromTo $ runDB $ selectList [] [Desc PaymentTimestamp]
Handler/Payment.hs:26:34:
Couldn't match expected type `User'
with actual type `HandlerT site0 IO User'
In the first argument of `userIdent', namely `from'
In the expression: userIdent from
In a stmt of a 'do' block: (key, val, userIdent from, userIdent to)
Handler/Payment.hs:26:50:
Couldn't match expected type `User'
with actual type `HandlerT site1 IO User'
In the first argument of `userIdent', namely `to'
In the expression: userIdent to
In a stmt of a 'do' block: (key, val, userIdent from, userIdent to)
有谁知道我做错了什么,或者如何解决这些错误? 我相信这与Extracting database field values inside a Handler 有很大关系。 但是我仍然无法通过上面链接中的信息解决问题。
更新
使用 Michael Snoyman 的回答编辑我的代码后,我的新 addFromTo 函数仍然出现类型错误:
addFromTo :: Entity Payment -> (Key Payment, Payment, Text, Text)
addFromTo (Entity key val) = do
from' <- runDB $ get404 $ paymentFrom val
to' <- runDB $ get404 $ paymentTo val
let from = userIdent from'
let to = userIdent to'
(key, val, from, to)
【问题讨论】:
看起来您正在使用 map 作为 Monadic 值。你可以试试payments <- mapM addFromTo $ runDB $ selectList [] [Desc PaymentTimestamp]
不幸的是,这只会导致更多错误。
这个方法对我也不起作用。但是another method 做到了。
【参考方案1】:
这里的问题是您将一元动作视为纯值。解决这个问题的更简单方法是使用 do-notation 和 slurp 运算符:
payments' <- runDB $ selectList [] [Desc PaymentTimestamp]
let payments = map addFromTo payments'
或者如果你想变得更漂亮(不一定推荐):
payments <- fmap (map addFromTo) $ runDB $ selectList [] [Desc PaymentTimestamp]
【讨论】:
谢谢,您的解决方案非常适合检索付款列表。因此,如果我理解正确,slurp 运算符会提取 monad 中包含的纯值,然后可以将“常规”函数应用于该值。但是,如果我尝试将此推理应用于我的 addFromTo 函数,我仍然会得到 HandlerT 类型不匹配。也许由于不同的原因,我在 addFromTo 中遇到了这些错误?我把我的新 addFromTo 函数放在我原来的问题中。以上是关于如何在 Yesod / Persistent 中正确使用 runDB的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Yesod/Persistent 访问两个不同的数据库服务器?
如何从 Yesod 的 Persistent 中获取原始键值?