Yesod 中的复合主键

Composite Primary Key in Yesod

我是 Haskell 的新手,现在已经尝试使用 yesod 大约一个星期了。我一直在尝试连接到在 sqlite 中具有复合主键的现有数据库。我设法让代码作为独立应用程序与 Database.Persist.Sqlite 一起使用。

这是使用 persistent-sqlite.

作为独立应用程序运行的代码
{-# LANGUAGE EmptyDataDecls             #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveGeneric #-}
import           Control.Monad.IO.Class  (liftIO)
import           Database.Persist
import           Database.Persist.Sqlite
import           Database.Persist.TH
import           System.Environment
import           Data.Text (Text,pack)
import           Data.Time (UTCTime)

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
    Movie
    title Text maxlen=20
    year Int maxlen=11
    genre Text Maybe maxlen=128
    mpaa Text Maybe maxlen=16
    director Text Maybe maxlen=128
    actors Text Maybe maxlen=512
    description Text Maybe maxlen=512
    path Text Maybe maxlen=128
    codec Text Maybe maxlen=32
    length Int Maybe maxlen=11
    poster Text Maybe maxlen=128
    added UTCTime default=CURRENT_TIMESTAMP
    Primary title year
    deriving Show
|]

main :: IO ()
main = do
  (path:args) <- getArgs
  movies <- runSqlite (pack path) $ do
    runMigration migrateAll
    selectList [] [Desc MovieTitle]

  mapM_ print movies

这是一个人为的例子,但它说明了我的观点。我想要一个由 titleyear 组成的复合主键。一切都编译得很好, table 是用我需要的模式创建的。当我尝试在带有 persistent-sqlite 的 yesod 应用程序中使用上面定义的 Movie 类型时,出现以下编译时错误

Foundation.hs:35:1:
    No instance for (PathPiece MovieId)
      arising from a use of ‘toPathPiece’
    In the first argument of ‘(:)’, namely ‘(toPathPiece dyn_apvA)’
    In the second argument of ‘(:)’, namely
      ‘((toPathPiece dyn_apvA) : [])’
    In the expression:
      ((Data.Text.pack "entry") : ((toPathPiece dyn_apvA) : []))

Foundation.hs:35:1:
    No instance for (PathPiece MovieId)
      arising from a use of ‘fromPathPiece’
    In the expression: fromPathPiece
    In the pattern: fromPathPiece -> Just dyn_apwt
    In the pattern: (:) (fromPathPiece -> Just dyn_apwt) []

我被困在这一点上,无法自行继续。我试过搜索,但我只能在不使用 yesod 的情况下找到复合主键的工作示例。我觉得应该可以做到,因为仅使用 persistent-sqlite 的复合主键有效。

下面是 Movie 类型在 config/models

中的定义方式
Movie
    title Text maxlen=20
    year Int maxlen=11
    genre Text Maybe maxlen=128
    mpaa Text Maybe maxlen=16
    director Text Maybe maxlen=128
    actors Text Maybe maxlen=512
    description Text Maybe maxlen=512
    path Text Maybe maxlen=128
    codec Text Maybe maxlen=32
    length Int Maybe maxlen=11
    poster Text Maybe maxlen=128
    added UTCTime default=CURRENT_TIMESTAMP
    Primary title year
    deriving

我自己并没有真正使用过复合主键功能,但这个错误对我来说很有意义。对于任意组合键, Text 没有明显的序列化,因此持久性不会为您生成它。如果您想在 URL 中使用 MovieId,则需要手动定义 PathPiece 实例,它只是一对用于与 Text 相互转换的函数。