我在设置一个简单的概念证明服务API时遇到了一个问题。这是我的用户数据类型和API类型:
data User = User { id :: Int, first_name :: String, last_name :: String } deriving (Eq, Show, Generic)
instance FromRow User
instance ToRow User
$(deriveJSON defaultOptions ''User)
type API = "users" :> ReqBody '[JSON] User :> Post '[JSON] User这里的处理程序方法使用postgresql-simple,如下所示:
create u = liftIO $ head <$> returning connection "insert into users (first_name, last_name) values (?,?) returning id" [(first_name u, last_name u)]已经省略了诸如连接到db和路由方法之类的样板代码。问题是,如果我发出POST请求,我想要做的是创建一个新用户,因此我将提供JSON:
{ "first_name": "jeff", "last_name": "lebowski" }但是我的程序在运行时会失败,因为
Error in $: When parsing the record User of type Lib.User the key id was not present. 这是有意义的,因为API指定了一个具有id字段的用户。但是我不想在请求中传递一个假的id (因为它们是由postgres按顺序分配的),因为那是很粗糙的。我也不能将id字段移出用户数据类型,因为当向不同的端点发出GET请求时,postgres-simple会因为模型数据库不匹配而失败(这做了一件显而易见的事情: get by id。不包括在上面)。我在这里做什么?编写自定义FromJson实例?我已经尝试将Data.Aeson.TH选项标志omitNothingFields设置为True,并将id字段设置为Maybe Int,但同样也不起作用。任何建议都将不胜感激。
发布于 2016-08-08 08:08:55
首先,您需要了解用户和表中对应于此用户的行是两个不同的东西。
行有id,用户没有。例如,您可以想象在不处理ids的情况下比较两个用户,或者考虑它们是否已保存。
一旦被说服,你将不得不向类型系统解释这一点,否则你将不得不处理可能的字段,我认为这不是解决方案。
有些人谈到模板Haskell,我认为它在这里有点夸张,你需要首先解决这个问题。
您可以做的是使用数据类型来表示数据库中保存的行。让我们称它为实体。
newtype PrimaryKey = PrimaryKey Int
data Entity b = Entity PrimaryKey b然后,在数据库中保存用户行的函数可以接受user作为参数,并返回一个PrimaryKey (当然,在数据库monad中)。从数据库读取数据的其他函数将使用Entity User返回一些内容
您的字段声明不会重复,因为您重用了User类型作为参数。
您必须相应地调整FromRow/ToRow和FromJSON/ToJSON。
https://stackoverflow.com/questions/38818478
复制相似问题