如何创建查询数据库的自定义字段? [英] How to create a custom field which queries the database?

查看:632
本文介绍了如何创建查询数据库的自定义字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Yesod的新手,希望创建一个自定义字段,我需要查询。



我的模型如下:

  Article 
artname文本
title文本
正文Text
Parent ArticleId也许

UniqueArt artname

类型化

我想创建用户输入 artname 而不是数字ID的父字段,但它将成为存储在数据库中的真实ID。



我不能使用 checkMMap ,因为反转函数在IO之外工作。



从我对字段处理的理解中, fieldParse 获取用户输入的值,并尝试将其转换为ArticleId,同时 fieldView 接受ArticleId并显示更人性化的版本。



到目前为止,我发现了以下内容:

  parentField :: Field sub ArticleId 
parentField = Field
{fieldParse = \rawVals _ - >
let(name:[])= rawVals
marticle< - runDB $ getBy(UniqueArt name)
case b
Nothing - >返回$(Left。SomeMessage)(Article name invalid。:: Text)
只是文章 - > return $(Right。Just)(entityKey article)

,fieldView = \idAttr nameAttr attrs eResult isReq - >
case
的eResult右键 - > do
marticle< - runDB $ get key
let name = case marticle of
Just article - >右(文章名称文章)
无 - > Left(Article key invalid。:: Text)

(fieldView textField)idAttr nameAttr attrs name isReq

Left _ - > (fieldView textField)idAttr nameAttr attrs eResult isReq
}

GHC不喜欢 marticle< - runDB $ get key 行,并给我以下错误:

 无法将类型'HandlerT site1 IO'
与'WidgetT(HandlerSite sub)IO'
预期类型:HandlerT site1 IO也许文章)
- > (也许Article - > HandlerT site1 IO())
- > WidgetT(HandlerSite sub)IO()
实际类型:HandlerT site1 IO(可能为文章)
- > (也许Article - > HandlerT site1 IO()) - > HandlerT site1 IO()
相关绑定包括
parentField :: Field sub ArticleId
(绑定在Handler / Article.hs:39:1)
在'do 'block:marticle< - runDB $ get key
在表达式中:
do {marticle< - runDB $ get key;
让name = ...;
(fieldView textField)idAttr nameAttr attrs name isReq}
在另一种情况下:
右键
- >做{marticle< - runDB $ get key;
让name = ...;
(fieldView textField)idAttr nameAttr attrs name isReq}

任何想法?它是一个 lift 我忘了吗?

解决方案

为了能够在 fieldParse fieldView 中执行查询,我需要进行一些调整:


  • parentField 签名必须完全指定。需要设置 YesodPersist YesodPersistBackend 约束,因为 runDB 调用。

  • fieldView 中的查询需要转换为 Widget Widget 的函数内部工作。这就是为什么要使用 handlerToWidget 函数。

  • 原始代码基于 textField 字段,但是这强加了其他限制。因此,我定义了自己的 whamlet

  • 缺少 fieldEnctype



以下是更新过的源代码:

  parentField :: YesodPersist网站
=> YesodPersistBackend网站〜SqlBackend
=> RenderMessage(HandlerSite(HandlerT site IO))FormMessage
=> Field(HandlerT site IO)ArticleId
parentField = Field
{fieldParse = \rawVals _ - > (名称:[])= rawVals
articleM< - runDB $ getBy(UniqueArt name)
return $ case articleM of
Nothing - > (Left。SomeMessage)(Article name invalid。:: Text)
只是文章 - > (Right。Just)(entityKey article)

,fieldView = \ name name attrs resultE isReq - >
案例resultE of
右键 - >做
articleM< - handlerToWidget。 runDB $ get key
let value = case article of
Just article - >右(文章名称文章)
无 - > Left(Article key invalid。:: Text)

parentHtml ident name attrs value isReq

Left err - > parentHtml ident name attrs(Left err)isReq
,fieldEnctype = UrlEncoded
}
where parentHtml ident name attrs val isReq =
[whamlet | $ newline never
<输入id =#{ident}
name =#{name}
* {attrs}
type =text
:isReq:required
value =#{or id id val}>
|]


I’m new to Yesod and would like to create a custom field in which I need to do a query.

My model is the following:

Article
    artname     Text
    title       Text
    body        Text
    parent      ArticleId Maybe

    UniqueArt   artname

    deriving    Typeable

I want to create a "parent field" in which the user enters an artname instead of a numerical id, but it will be the real id which will be stored in the database.

I cannot use checkMMap since the invert function works outside of IO.

From what I understood of the field processing, fieldParse takes the value entered by the user and tries to convert it to an ArticleId while fieldView takes an ArticleId and shows a more human version.

What I’ve come up until now is the following:

parentField :: Field sub ArticleId
parentField = Field
    { fieldParse = \rawVals _ -> do
            let (name:[]) = rawVals
            marticle <- runDB $ getBy (UniqueArt name)
            case marticle of
                Nothing      -> return $ (Left . SomeMessage) ("Article name invalid." :: Text)
                Just article -> return $ (Right . Just) (entityKey article)

    , fieldView = \idAttr nameAttr attrs eResult isReq ->
            case eResult of
                Right key -> do
                    marticle <- runDB $ get key
                    let name = case marticle of
                                   Just article -> Right (articleArtname article)
                                   Nothing      -> Left ("Article key invalid." :: Text)

                    (fieldView textField) idAttr nameAttr attrs name isReq

                Left _ -> (fieldView textField) idAttr nameAttr attrs eResult isReq
    }

GHC doesn’t like the marticle <- runDB $ get key line and gives me the following error:

Handler/Article.hs:50:21:
    Couldn't match type ‘HandlerT site1 IO’
                  with ‘WidgetT (HandlerSite sub) IO’
    Expected type: HandlerT site1 IO (Maybe Article)
                   -> (Maybe Article -> HandlerT site1 IO ())
                   -> WidgetT (HandlerSite sub) IO ()
      Actual type: HandlerT site1 IO (Maybe Article)
                   -> (Maybe Article -> HandlerT site1 IO ()) -> HandlerT site1 IO ()
    Relevant bindings include
      parentField :: Field sub ArticleId
        (bound at Handler/Article.hs:39:1)
    In a stmt of a 'do' block: marticle <- runDB $ get key
    In the expression:
      do { marticle <- runDB $ get key;
           let name = ...;
           (fieldView textField) idAttr nameAttr attrs name isReq }
    In a case alternative:
        Right key
          -> do { marticle <- runDB $ get key;
                  let name = ...;
                  (fieldView textField) idAttr nameAttr attrs name isReq }

Any idea ? Is it a lift I forgot ?

解决方案

In order to be able to do queries inside fieldParse and fieldView, I needed some adjustments:

  • the parentField signature must be fully specified. The YesodPersist and YesodPersistBackend constraints needs to be set because of the runDB calls.
  • the query inside fieldView needs to be translated to a Widget because it is working inside a function that outputs a Widget. That’s why the handlerToWidget function is used.
  • the original code was based on the textField field but this imposed other constraints. Therefore I defined my own whamlet.
  • the fieldEnctype was missing.

Here is the updated source code:

parentField :: YesodPersist site
            => YesodPersistBackend site ~ SqlBackend
            => RenderMessage (HandlerSite (HandlerT site IO)) FormMessage
            => Field (HandlerT site IO) ArticleId
parentField = Field
    { fieldParse = \rawVals _ -> do
            let (name:[]) = rawVals
            articleM <- runDB $ getBy (UniqueArt name)
            return $ case articleM of
                Nothing      -> (Left . SomeMessage) ("Article name invalid." :: Text)
                Just article -> (Right . Just) (entityKey article)

    , fieldView = \ident name attrs resultE isReq ->
            case resultE of
                Right key -> do
                    articleM <- handlerToWidget . runDB $ get key
                    let value = case articleM of
                                   Just article -> Right (articleArtname article)
                                   Nothing      -> Left ("Article key invalid." :: Text)

                    parentHtml ident name attrs value isReq

                Left err -> parentHtml ident name attrs (Left err) isReq
    , fieldEnctype = UrlEncoded
    }
    where parentHtml ident name attrs val isReq =
            [whamlet|$newline never
                <input id="#{ident}"
                       name="#{name}"
                       *{attrs}
                       type="text"
                       :isReq:required
                       value="#{either id id val}">
            |]

这篇关于如何创建查询数据库的自定义字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆