我能找到的关于Persistent的所有教程和参考都详细描述了Persistent如何在DSL中的单个定义中自动创建新的数据类型,模式,迁移等。但是,我无法找到有关如何使Persistent处理现有数据类型的解释。
一个例子:假设我有一个已经存在的Haskell模块用于某些游戏逻辑。它包括玩家的记录类型。 (它意味着通过镜头使用,因此是下划线。)
data Player = Player { _name :: String
, _points :: Int
-- more fields ...
}
$(makeLenses ''Player)
问题:使用Persistent在数据库中存储此类型的规范方法是什么?我可以实现一些类型类吗?或者我应该通过Persistent最好地定义一个新类型,例如
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
PlayerEntry
name Text
points Int
|]
然后在这些类型之间手动映射?
playerToEntry :: Player -> PlayerEntry
playerToEntry pl = PlayerEntry (pl^.name) (pl^.points)
entryToPlayer :: PlayerEntry -> Player
entryToPlayer e = Player (name e) (points e)
答案 0 :(得分:2)
来自:http://www.yesodweb.com/book/persistent
{-# LANGUAGE TemplateHaskell #-}
module Employment where
import Database.Persist.TH
data Employment = Employed | Unemployed | Retired
deriving (Show, Read, Eq)
derivePersistField "Employment"
derivePersistField
函数是模板Haskell魔法,它使它工作。
注意,您需要在derivePersistField
的单独文件中执行mkPersist
操作以避免TH阶段错误。
答案 1 :(得分:1)
我解决这个问题的方法是通过Yesod的mkPersist
添加一个新类型,然后手动编组。
config/models
:
PlayerEntry
name Text
points Int
created UTCTime default=CURRENT_TIMESTAMP
Marshalling.hs
:
fromPlayerEntry :: PlayerEntry -> Player
fromPlayerEntry PlayerEntry {..} = Player { name = playerName
, points = playerPoints
}
createPlayerEntry :: Text -> YesodDB App (Entity PlayerEntry)
createPlayerEntry name = do
currentTime <- liftIO getCurrentTime
let player = PlayerEntry { playerName = name
, playerPoints = 0
, playerCreated = currentTime
}
playerId <- insert player
return $ Entity playerId player
updatePlayerEntry :: PlayerEntryId -> Player -> YesodDB App ()
updatePlayerEntry playerId Player {..} =
update playerId [ PlayerName =. name
, PlayerPoints =. points
]
一个可能的优点是您可以在表中包含内部记录中不需要的字段。在我的示例中,将创建日期附加到播放器很有用。但是,这仅用于Web界面层,它从未在内部游戏逻辑中使用,后者定义了Player
类型。但是,由于手动编组,我可以将该字段添加到同一个数据库表中。