Reputation: 43852
I'm trying to use Persistent with Servant, so I don't have the luxury of automatically parsing URL segments into Persistent keys. Instead, I've set up my routes to require an Int64
, and I want to retrieve a record using that to perform primary key lookup.
Everything I've found points to using toSqlKey
to convert an integer to a key, so I tried to write a very simple function that would do that for me:
runDB :: (MonadBaseControl IO m, MonadIO m) => (SqlPersistT (NoLoggingT (ResourceT m))) a -> m a
runDB actions = do
filename <- liftIO $ getEnv "SQLITE_FILENAME"
runSqlite (pack filename) actions
getRecordByKey :: Int64 -> IO (Maybe (Entity Record))
getRecordByKey recordId = runDB $ get (toSqlKey recordId)
Unfortunately, this didn't work; I got the following type error:
Couldn't match expected type ‘PersistEntityBackend
(Entity Record)’
with actual type ‘SqlBackend’
In the second argument of ‘($)’, namely ‘get (toSqlKey recordId)’
In the expression: runDB $ get (toSqlKey recordId)
In an equation for ‘getRecordByKey’:
getRecordByKey recordId = runDB $ get (toSqlKey recordId)
I sort of understand this error—I looked up the types for get
and toSqlKey
, and they include the relevant constraints:
get :: (MonadIO m, backend ~ PersistEntityBackend val, PersistEntity val) => Key val -> ReaderT backend m (Maybe val)
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record
If I understand correctly, backend
and PersistEntityBackend val
need to be the same type, but toSqlKey
enforces the SqlBackend
constraint, so the types mismatch. My intuition tells me that PersistentEntityBackend (Entity Record)
should be SqlBackend
, but evidently I'm wrong there. I don't know why or how, though.
Anyway, I don't know if I'm right or wrong in that analysis, but either way, I'm not sure how to fix this or what the proper way of doing this is. How can/should I be getting a record from my database given an integer?
Upvotes: 0
Views: 616
Reputation: 9331
And what about making Key record
directly an instance of FromText
/ToText
and use the keys in the URL directly?
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
instance ToBackendKey SqlBackend record => FromText (Key record) where
fromText k = toSqlKey <$> fromText k
instance ToBackendKey SqlBackend record => ToText (Key record) where
toText = toText . fromSqlKey
Upvotes: 1
Reputation: 52280
this works for me (may depend on the versions of your packages ... sadly):
{-# LANGUAGE FlexibleContexts #-}
module Stackoverlflow where
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Logger(NoLoggingT)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Resource (ResourceT)
import Data.Int (Int64)
import Data.Text (pack)
import Database.Persist.Class (ToBackendKey, get)
import Database.Persist.Sql (SqlBackend, SqlPersistT, toSqlKey)
import Database.Persist.Sqlite(runSqlite)
import Database.Persist.Types (Entity)
import System.Environment (getEnv)
runDB :: (MonadBaseControl IO m, MonadIO m) =>
(SqlPersistT (NoLoggingT (ResourceT m))) a -> m a
runDB actions = do
filename <- liftIO $ getEnv "SQLITE_FILENAME"
runSqlite (pack filename) actions
getRecordByKey :: (MonadIO m, ToBackendKey SqlBackend val, MonadBaseControl IO m) =>
Int64 -> m (Maybe val)
getRecordByKey recordId = runDB $ get (toSqlKey recordId)
as you can see I just added alot of types annotations (well GHC did after I removed the signatures and ask it to tell me ;))
also note that I don't have your Record
so you should easily be able to get rid of the ... val
stuff!
Upvotes: 2