如何在Yesod中为外键的应用表单定义字段?

gur*_*aku 6 haskell yesod

如果我们在模型文件中定义了2个简单对象,例如: -

Person
  name Text
  Age Int
Book
  title Text
  author Text
Run Code Online (Sandbox Code Playgroud)

我们可以为Book定义一个适用表格: -

addBookForm = renderDivs $ Book
  <$> areq textField "title" Nothing
  <*> areq textField "author" Nothing
Run Code Online (Sandbox Code Playgroud)

但是,如果我们想要将作者从一个文本字段更改为一个人的id,则:

Book
  title Text
  author PersonId
Run Code Online (Sandbox Code Playgroud)

然后上面的表格将无法编译,出现此错误: -

Couldn't match expected type `KeyBackend Database.Persist.GenericSql.Raw.SqlBackend Person' with actual type `Text'
Expected type: Field
                 sub0
                 master0
                 (KeyBackend Database.Persist.GenericSql.Raw.SqlBackend Person)
  Actual type: Field sub0 master0 Text
In the first argument of `areq', namely `textField'
In the second argument of `(<*>)', namely
  `areq textField "author" Nothing'
Run Code Online (Sandbox Code Playgroud)

我们现在如何定义作者字段?我们需要使用monadic形式吗?

谢谢 !

bps*_*bps 6

错误消息表示您尝试使用Text(来自字段结果)作为Key.

您可以使用checkMMap包装textField并修改结果:

addBookForm = renderDivs $ Book
  <$> areq textField "title" Nothing
  <*> (entityKey <$> areq authorField "author" Nothing)
    where
  authorField = checkMMap findAuthor (personName . entityVal) textField

  findAuthor name = do
    mperson <- runDB $ selectFirst [PersonName ==. name] []
    case mperson of
       Just person -> return $ Right person
       Nothing     -> return $ Left ("Person not found." :: Text)
Run Code Online (Sandbox Code Playgroud)

findAuthor函数获取简单,如果你添加一个独特的构造函数的Person领域:

Person
  name Text
  ...
  UniquePerson name
Run Code Online (Sandbox Code Playgroud)

然后selectFirst ...你可以做而不是

mperson <- runDB $ getBy $ UniquePerson name
Run Code Online (Sandbox Code Playgroud)