If we have defined 2 simple objects in our models file, for example :-
Person
name Text
Age Int
Book
title Text
author Text
We can define an applicative form for Book as :-
addBookForm = renderDivs $ Book
<$> areq textField "title" Nothing
<*> areq textField "author" Nothing
However, if we want to change the author from just a text field, to the id of a person, as :-
Book
title Text
author PersonId
Then the above form won't compile, with this error :-
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'
How do we now define the author field ? Do we need to use a monadic form ?
Thanks !
The error message means you are trying to use Text (from the field result) as a Key.
You can use checkMMap
to wrap the textField and modify the result:
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)
The findAuthor
function gets simpler if you add a unique constructor to the Person field:
Person
name Text
...
UniquePerson name
Then instead of selectFirst ...
you can do
mperson <- runDB $ getBy $ UniquePerson name
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With