I'm trying to learn the Persistent Library.
I have two files one with all the types I've defined for my project (diet tracker)
Report.hs
data FoodEntry = FoodEntry { report :: Report
, date :: UTCTime
}
data Report = Report { ...
...
}
and another file used to generate tables
Storage.hs
import Database.Persist.Sql
import qualified Database.Persist.TH as PTH
PTH.share [ PTH.mkPersist PTH.sqlSettings
, PTH.mkMigrate "migrateAll"
]
[PTH.persistLowerCase|
FoodEntry sql=entries
report Report
date UTCTime default=now()
UniqueDate date
Report sql=reports
name Text
reportingUnit Text
nutrients [Nutrient]
Nutrient sql=nutrients
nutrientName Text
nutrientUnit Text
nutrientValue Double Maybe
deriving Show Read
|]
and a file that handles migration
import Control.Monad.Logger
import Database.Persist
import Database.Persist.Postgresql
import Types.Storage
connString :: ConnectionString
connString = "host=127.0.0.1 port=5432 user=postgres dbname=postgres password=password"
runAction
:: (MonadUnliftIO m, IsPersistBackend r,
BaseBackend r ~ SqlBackend) =>
ConnectionString -> ReaderT r (LoggingT m) a -> m a
runAction connectionString action = runStdoutLoggingT
$ withPostgresqlConn connectionString
$ \backend ->
runReaderT action backend
migrateDB :: IO ()
migrateDB = runAction connString (runMigration migrateAll)
In my [ProjectName.hs]
file which main
calls on
args <- parseArgs -- args - optparse-applicative
if generateDB args == Just True
then migrateDB
else case (...) of
... -> ...
... -> ...
(s, n, p, Just True) -> undefined
for the last case I want to add the user entry to the database.
I also have a function searchReport
that consumes s
, n
& p
to returns a Report
type.
I want to do something like this
insertReport :: (MonadIO m) => UTCTime -> Report -> SqlPersistT m (Key FoodEntry)
insertReport time report' = insert (FoodEntry report' time)
do date' <- Data.Time.Clock.getCurrentTime :: IO UTCTime
report' <- searchReport s n p :: IO Report
insertReport $ date' report'
But there are (AFAIK) two problem with this
The type of Report.FoodEntry
would not match the type
Storage.FoodEntry
I could write a function that'd convert one to
the other but I wonder if there's a better way of doing that.
If I were to test the function in that do block like this
(s, n, p, Just True) -> insertReport undefined undefined
I get error
• Couldn't match type ‘ReaderT
Database.Persist.Sql.Types.Internal.SqlBackend m0’
with ‘IO’
Expected type: IO ()
Actual type: persistent-2.9.2:Database.Persist.Sql.Types.SqlPersistT
m0
(persistent-2.9.2:Database.Persist.Class.PersistEntity.Key
Types.Storage.FoodEntry)
To run insertReport
from IO
monad you have to call runAction connectionString insertReport
. This is what actually converts SqlPersistT
to IO
.
As for difference between Report.FoodEntry
and Storage.FoodEntry
- why do you have two data types in first place? The entity you declared in PTH.share
quasiquoter is also a valid Haskell data type, so you can use it like any other.
However, if you really need to have 2 different FoodEntry
es, then yes, you would need to write a funtion that converts between them.