Haskell Yesod中如何取出Handler monad内的值展示在模板
问题解决方法
错误原因
你遇到的报错本质是模板渲染层无法执行Handler Monad中的逻辑:Hamlet模板仅支持传入可以直接转为Markup的纯值(比如Text、数字等),你直接在模板中调用getCommentWriter,它的返回类型是Handler Text(一个还未执行的数据库查询动作),自然不存在对应的ToMarkup实例,因此触发了编译错误。
核心解决思路
所有数据库查询、业务逻辑处理都要在Handler层完成,预处理好模板需要的全部数据后再传入模板渲染,不要在模板中执行任何IO/数据库操作。
具体修改步骤
1. 调整Handler层逻辑,预查询每个评论对应的作者
你需要先把评论列表和对应的作者名组装好,同时要处理manCommentWriter为Nothing的边界情况(避免fromJust引发运行时崩溃):
-- 辅助函数:给单条评论拼接上作者名 getCommentWithAuthor :: Entity ManComment -> Handler (Entity ManComment, Text) getCommentWithAuthor commentEnt@(Entity _ com) = case manCommentWriter com of -- 处理作者为空的情况,避免fromJust崩溃 Nothing -> return (commentEnt, "匿名用户") Just uid -> do user <- runDB $ get404 uid return (commentEnt, userIdent user) getManDetailsR :: ManifestationId -> Handler Html getManDetailsR mid = do (ui, user) <- requireAuthPair comments <- runDB $ getComFromMan mid -- 遍历所有评论,预查作者,组装成模板可直接用的结构 commentsWithAuthors <- mapM getCommentWithAuthor comments defaultLayout $ do setTitle "Manifestation details" $(widgetFile "man-details")
2. 修改Hamlet模板,直接使用预组装好的作者名
把原来遍历comments的逻辑改成遍历预先生成的commentsWithAuthors,直接取已经查好的作者名字段即可:
$if null commentsWithAuthors <h4>There is not comments! $else $forall (Entity cid com, authorName) <- commentsWithAuthors <form method=post action=@{DeleteManCommentR mid cid}> <li .list-group-item> <div class="row"> <div class="col-xs-10 col-md-11"> <div> <div .mic-info> By: <a href=@{ProfileR}>#{authorName}</a>
优化建议
如果评论量较大,上述方案会出现N+1查询问题(1次查询评论列表,N次查询每个评论的作者),你可以使用Yesod的Esqueleto库编写关联查询,一次性把评论和对应的作者信息全部查出,大幅提升接口性能。
内容的提问来源于stack exchange,提问作者jovanKg
相关产品推荐
相关产品推荐

