play slick updating enumeration column

671 views Asked by At

I'm having trouble figuring out how to update a column with type enumeration using play-slick.

Here's my enum and case class:

object TestStatus extends Enumeration {
  type TestStatus = Value
  val Status1 = Value("Status1")
}
case class Test (
  id: String,
  status: TestStatus
)

and the table mapping:

class Tests(tag: Tag) extends Table[Test](tag, "tests") {
  implicit val statusColumn = MappedColumnType.base[TestStatus, String](_.toString, TestStatus.withName)
  override def * = (id, status) <> ((Test.apply _).tupled, Test.unapply)
  val id = column[String]("id", 0.PrimaryKey)
  val status = column[TestStatus]("status")
}

when I try to go and update a Tests row, I get an error:

object TestQueries extends TableQuery[Tests](new Tests(_)) {
  def updateStatus(id: String, newStatus: TestStatus) = {
    TestQueries.filter(_.id === id).map(_.status).update(newStatus)
  }
}

[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection,
[error]  you use an unsupported type in a Query (e.g. scala List),
[error]  or you forgot to import a driver api into scope.
[error]   Required level: slick.lifted.FlatShapeLevel
[error]      Source type: slick.lifted.Rep[models.TestStatus.Value]
[error]    Unpacked type: T
[error]      Packed type: G
[error]     TestQueries.filter(_.id === id).map(_.status).update(newStatus)
[error]                                        ^

IntelliJ is showing that TestQueries.filter(_.id === id).map(_.status) has type Query[Nothing, Nothing, Seq], which makes me think the problem is with the specific column rather than with the update function.

Updating the id works fine using the same structure.

1

There are 1 answers

0
shayan On

You need to define the custom column type of TestStatus.Value. This is how slick allows you to build a custom column type by mapping it to an already supported type:

implicit def testStatCT: BaseTypedType[TestStatus.Value] = 
  MappedColumnType.base[TestStatus.Value, String](
    enum => enum.toString, str => TestStatus.withName(str)
  )

this implicit needs to be imported wherever implicit resolutions such as the one in your example fail (or better yet defined in the TestStatus object so that it's always available) this way slick can have evidence that TestStatus.Value is a BaseTypedType which basically just means that something is a supported column type.

For further read on column mapping you can look at Slick Documentation