Inserting entry into database using PostGreSQL & Persistent

430 Views Asked by At

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

  1. 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.

  2. 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)                                                             
    
1

There are 1 best solutions below

1
arrowd On BEST ANSWER

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 FoodEntryes, then yes, you would need to write a funtion that converts between them.