使用Servant和Persistent表示JSON中的外键关系

rew

今天早上,我跟随着这个有趣的教程,介绍了如何使用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
hao

对于某些记录类型rEntity r是包含Key r的数据类型r您可以将其视为一个多联的元组(Key r, r)

(您可能会想知道这Key r什么。不同的后端具有不同的种类Key r。对于Postgres,它将是64位整数。对于MongoDB,它具有对象ID。文档中有更详细的说明。它是一种抽象,允许Persistent支持多个数据存储)

您的问题是您有一个Key User我们的策略是为您提供一个Entity User,从中我们可以拿出一个User幸运的是,只需进行一次数据库访问即可轻松地Key User转到是一种模式匹配。Entity UserselectFirstEntity UserUser

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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章