今天早上,我跟随着这个有趣的教程,介绍了如何使用Servant构建简单的API服务器。
在本教程的最后,作者建议添加一个Blog类型,因此我想尝试一下,但是我在尝试实现并序列化扩展于本教程逻辑的外键关系时遇到了麻烦(也许很重要此处公开:我对仆人和持久性都是新手。
这是我的持久性定义(我添加了Post
):
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
name String
email String
deriving Show
Post
title String
user UserId
summary String
content String
deriving Show
|]
本教程Person
为Servant API构建了一种单独的数据类型,因此我还添加了一个名为Article
:
-- API Data Types
data Person = Person
{ name :: String
, email :: String
} deriving (Eq, Show, Generic)
data Article = Article
{ title :: String
, author :: Person
, summary :: String
, content :: String
} deriving (Eq, Show, Generic)
instance ToJSON Person
instance FromJSON Person
instance ToJSON Article
instance FromJSON Article
userToPerson :: User -> Person
userToPerson User{..} = Person { name = userName, email = userEmail }
但是,现在,当我尝试创建将aPost
变成an的函数时Article
,我陷入了处理User
外键的困境:
postToArticle :: Post -> Article
postToArticle Post{..} = Article {
title = postTitle
, author = userToPerson postUser -- this fails
, summary = postSummary
, content = postContent
}
我尝试了很多方法,但是以上方法似乎很接近我想要的方向。但是由于以下错误,它无法编译:
Couldn't match expected type ‘User’
with actual type ‘persistent-2.2.2:Database.Persist.Class.PersistEntity.Key
User’
In the first argument of ‘userToPerson’, namely ‘postUser’
In the ‘author’ field of a record
归根结底,我不确定自己到底是PersistEntity.Key User
什么,而我那错误的谷歌搜索并没有使我更加接近。
如何处理这种外键关系?
工作版本
感谢haoformayor编辑并回答
postToArticle :: MonadIO m => Post -> SqlPersistT m (Maybe Article)
postToArticle Post{..} = do
authorMaybe <- selectFirst [UserId ==. postUser] []
return $ case authorMaybe of
Just (Entity _ author) ->
Just Article {
title = postTitle
, author = userToPerson author
, summary = postSummary
, content = postContent
}
Nothing ->
Nothing
对于某些记录类型r
,Entity r
是包含Key r
和的数据类型r
。您可以将其视为一个多联的元组(Key r, r)
。
(您可能会想知道这Key r
是什么。不同的后端具有不同的种类Key r
。对于Postgres,它将是64位整数。对于MongoDB,它具有对象ID。文档中有更详细的说明。它是一种抽象,允许Persistent支持多个数据存储)
您的问题是您有一个Key User
。我们的策略是为您提供一个Entity User
,从中我们可以拿出一个User
。幸运的是,只需进行一次数据库访问即可轻松地从Key User
转到。从到是一种模式匹配。Entity User
selectFirst
Entity User
User
postToArticle :: MonadIO m => Post -> SqlPersistT m (Maybe Article)
postToArticle Post{..} = do
authorMaybe <- selectFirst [UserId ==. postUser] []
return $ case authorMaybe of
Just (Entity _ author) ->
Article {
title = postTitle
, author = author
, summary = postSummary
, content = postContent
}
Nothing ->
Nothing
我们在上面假设了一个SQL后端,但是该函数也具有更通用的类型
postToArticle ::
(MonadIO m, PersistEntity val, backend ~ PersistEntityBackend val) =>
Post -> ReaderT backend m (Maybe Article)
如果您不使用SQL后端,可能需要哪些。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句