gbwey / persistent-odbc

uses persistent connecting via hdbc odbc
Other
17 stars 16 forks source link

Yesod #13

Open felipexpert opened 8 years ago

felipexpert commented 8 years ago

How can I easily use it with yesod through stack? (By the way, thank you so much for this repo, it was the only way I've found to use Persistent+Mysql+Windows)

gbwey commented 8 years ago

I just tried this and it looks like you would need to add at least these extra dependencies to stack.yaml and then run "stack build".

extra-deps:

Let me know if you need more info. Best, Grant

felipexpert commented 8 years ago

How can I implement YesodPersist in Yesod?

I tryed the following which doesn't compile:

-- How to run database actions.
instance YesodPersist App where
  runDB = runResourceT . runNoLoggingT . withODBCConn Nothing "dsn=mysql_test" . runSqlConn

For instance, I've found the following code from the Yesod book:

{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeFamilies               #-}
import           Control.Monad.Logger    (runNoLoggingT)
import           Data.Text               (Text)
import           Data.Time
import           Database.Persist.Sqlite
import           Yesod

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Link
    title Text
    url Text
    added UTCTime
|]

data App = App ConnectionPool

mkYesod "App" [parseRoutes|
/         HomeR    GET
/add-link AddLinkR POST
|]

instance Yesod App

instance RenderMessage App FormMessage where
    renderMessage _ _ = defaultFormMessage

instance YesodPersist App where
    type YesodPersistBackend App = SqlBackend
    runDB db = do
        App pool <- getYesod
        runSqlPool db pool

getHomeR :: Handler Html
getHomeR = defaultLayout
    [whamlet|
        <form method=post action=@{AddLinkR}>
            <p>
                Add a new link to
                <input type=url name=url value=http://>
                titled
                <input type=text name=title>
                <input type=submit value="Add link">
        <h2>Existing links
        ^{existingLinks}
    |]

existingLinks :: Widget
existingLinks = do
    links <- handlerToWidget $ runDB $ selectList [] [LimitTo 5, Desc LinkAdded]
    [whamlet|
        <ul>
            $forall Entity _ link <- links
                <li>
                    <a href=#{linkUrl link}>#{linkTitle link}
    |]

postAddLinkR :: Handler ()
postAddLinkR = do
    url <- runInputPost $ ireq urlField "url"
    title <- runInputPost $ ireq textField "title"
    now <- liftIO getCurrentTime
    runDB $ insert $ Link title url now
    setMessage "Link added"
    redirect HomeR

main :: IO ()
main = runNoLoggingT $ withSqlitePool "links.db3" 10 $ \pool -> liftIO $ do
    runSqlPersistMPool (runMigration migrateAll) pool
    warp 3000 $ App pool
gbwey commented 8 years ago

stack new abcd yesod-simple [ie create project with yesod-simple template just to setup all yesod dependencies]

then add these dependencies to the abcd.cabal file , persistent-odbc , esqueleto

add these extra dependencies to stack.yaml extra-deps:

run: stack build

Create this file: I dont have MySql on my machine so I used sqlite3 instead but it is similar: the key line is:
pool <- runStderrLoggingT $ createODBCPool (Just (Sqlite True)) "dsn=sqlitetest" 1 Just replace with MySql and you should be running fine

A minor thing, : but I noticed that you had: let conf = OdbcConf "DNS=mysql_test;" 1 "MySQL" [should be DSN=]

{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} import Control.Monad.Logger import Data.Text (Text) import qualified Database.Esqueleto as E import Database.Esqueleto ((^.)) import Yesod import qualified Data.Conduit.List as CL import Data.Conduit (($=)) import Prelude import Database.Persist.ODBC import Database.Persist.ODBCTypes import Database.Persist.MigrateSqlite import System.IO.Unsafe

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| Author name Text Blog author AuthorId title Text content Html |]

data App = App { persistConfig :: OdbcConf , connPool :: ConnectionPool } instance Yesod App

instance YesodPersist App where type YesodPersistBackend App = SqlBackend runDB = defaultRunDB persistConfig connPool

instance YesodPersistRunner App where getDBRunner = defaultGetDBRunner connPool

mkYesod "App" [parseRoutes| / HomeR GET /blog/#BlogId BlogR GET |]

getHomeR :: Handler TypedContent getHomeR = do let blogsSrc = E.selectSource $ E.from $ (blog E.InnerJoin author) -> do E.on $ blog ^. BlogAuthor E.==. author ^. AuthorId return ( blog ^. BlogId , blog ^. BlogTitle , author ^. AuthorName ) render <- getUrlRenderParams respondSourceDB typeHtml $ do sendChunkText "Blog posts

" getBlogR :: BlogId -> Handler Html getBlogR _ = error "Implementation left as exercise to reader" main :: IO () main = do -- Is this configuration right? -- let conf = OdbcConf "DNS=mysql_test;" 1 "MySQL" -- DRIVER=SQLite3 ODBC Driver;Database=c:\mydb.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0; -- let conf = OdbcConf "DRIVER=SQLite3 ODBC Driver;Database=mydb.db;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;" 1 "Sqlite {sqlite3619 = True}" pool <- runStderrLoggingT $ createODBCPool (Just (Sqlite True)) "dsn=sqlitetest" 1 flip runSqlPersistMPool pool $ do runMigration migrateAll ``` -- Fill in some testing data alice <- insert $ Author "Alice" bob <- insert $ Author "Bob" insert_ $ Blog alice "Alice's first post" "Hello World!" insert_ $ Blog bob "Bob's first post" "Hello World!!!" insert_ $ Blog alice "Alice's second post" "Goodbye World!" ``` warp 3000 App { persistConfig = undefined , connPool = pool }
felipexpert commented 8 years ago

It is working! There is just a issue, when I run the first time with the migrations, a error appears, but then (after commenting it) it works apparently properly: (the error is in the last line)

Run from outside a project, using implicit global project config Using resolver: lts-4.1 from implicit global project's config file: /home/geppetto/.stack/global-project/stack.yaml

getColumns cs=[Right (Left (Column {cName = DBName {unDBName = "name"}, cNull = False, cSqlType = SqlString, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing}))]

us=[]

old=[Right (Left (Column {cName = DBName {unDBName = "name"}, cNull = False, cSqlType = SqlString, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing}))]

fdefs=[]

getColumns cs=[Right (Left (Column {cName = DBName {unDBName = "author"}, cNull = False, cSqlType = SqlInt64, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing})),Right (Left (Column {cName = DBName {unDBName = "title"}, cNull = False, cSqlType = SqlString, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing})),Right (Left (Column {cName = DBName {unDBName = "content"}, cNull = False, cSqlType = SqlString, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing}))]

us=[]

old=[Right (Left (Column {cName = DBName {unDBName = "author"}, cNull = False, cSqlType = SqlInt64, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing})),Right (Left (Column {cName = DBName {unDBName = "title"}, cNull = False, cSqlType = SqlString, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing})),Right (Left (Column {cName = DBName {unDBName = "content"}, cNull = False, cSqlType = SqlString, cDefault = Nothing, cDefaultConstraintName = Nothing, cMaxLen = Nothing, cReference = Nothing}))]

fdefs=[] Migrating: ALTER TABLE blog ADD CONSTRAINT blog_author_fkey FOREIGN KEY(author) REFERENCES author(id) [Debug#SQL] ALTER TABLE blog ADD CONSTRAINT blog_author_fkey FOREIGN KEY(author) REFERENCES author(id); [] File.hs: Prelude.chr: bad argument: 5832776

gbwey commented 8 years ago

Unfortunately I don't have mysql and am unable to debug this. Best, Grant

felipexpert commented 8 years ago

Hello gbway, I am following another path. I am trying to integrate persistent-odbc with scotty, looking at this tutorial: http://adit.io/posts/2013-04-15-making-a-website-with-haskell.html

tutorial version using Postgrees:

import Database.Persist
import Database.Persist.Sqlite

runDb :: SqlPersist (ResourceT IO) a -> IO a
runDb query = runResourceT . withSqliteConn "dev.sqlite3" . runSqlConn $ query

but I am getting trouble in the following code:

import Prelude
import Database.Persist
import Database.Persist.ODBC

import Control.Monad.Logger (runNoLoggingT)
import Control.Monad.Trans.Resource (runResourceT, ResourceT)

runDb :: SqlPersist (ResourceT IO) a -> IO a
runDb = runResourceT . runNoLoggingT . (withODBCConn Nothing "dsn=newmyerp") . runSqlConn

But the signature doesn't match...