4

シンプルな概念実証のサーバント API を設定する際に問題が発生しています。これは私の User データ型と 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)]

データベースへの接続やルーティング メソッドなどのボイラープレート コードは省略されています。問題は、私が 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 フィールドを持つ User を指定したため、これは理にかなっています。しかし、リクエストで偽のIDを渡す必要はありません(postgresによって順次割り当てられるため)。また、id フィールドを User データ型から移動することもできません。これは、別のエンドポイントに GET 要求を行うときにモデルとデータベースの不一致が原因で postgres-simple が失敗するためです (これにより、ID による取得が行われます。上記には含まれていません)。 )。ここで何をすればいいですか?カスタム FromJson インスタンスを作成しますか? Data.Aeson.TH オプション フラグ omitNothingFields を True に設定し、id フィールドを Maybe Int に設定しようとしましたが、それもうまくいきませんでした。アドバイスをいただければ幸いです。

4

1 に答える 1

4

最初に、ユーザーと、このユーザーに対応するテーブル内の行は 2 つの異なるものであることを理解する必要があります。

行には ID がありますが、ユーザーにはありません。たとえば、ID を扱わずに 2 人のユーザーを比較したり、ユーザーが保存されているかどうかを比較したりすることを想像できます。

納得したら、これを型システムに説明するか、Maybe フィールドに対処する必要がありますが、これはここでは解決策ではないと思います。

Template Haskell について話している人もいましたが、ここではやり過ぎだと思います。最初に問題を解決する必要があります。

できることは、データ型を使用して、データベースに保存された行を表すことです。それをエンティティと呼びましょう。

newtype PrimaryKey = PrimaryKey Int

data Entity b = Entity PrimaryKey b

次に、データベースにユーザー行を保存する関数は、useras パラメーターを取り、 a を返すことができPrimaryKeyます (もちろん、データベースモナドでは)。データベースから読み取る他の関数は、次を使用して何かを返しますEntity User

User 型をパラメーターとして再利用しているため、フィールド宣言は複製されません。

それに応じて FromRow/ToRow と FromJSON/ToJSON を調整する必要があります。

于 2016-08-08T00:08:55.237 に答える