如何在 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 &lt;- 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 中获取原始键值?

如何让 yesod-persistent 识别我的 aeson 解析实体数组的类型?

退出代码 11 安装 Persistent (Yesod)

Yesod/Persistent 实体派生 Show

Yesod Persistent - 如何将 Day 与 UTCTime 进行比较? (如何转换它们?)