Play Framework: Cannot write findById function using SLICK - scala

I am trying to write a findById(pk: Long) and an update() function for a model I have created using SLICK. However in my findById method it says returns a compilation error of "value filter is not a member of object models.About" and highlights the model name About in the findById method.
package models
//import scala.slick.driver.PostgresDriver.simple._
import play.api.db.slick.Config.driver._
case class About(
id:Option[Long],
name: String,
subheading: String,
about: String
)
object About extends Table[About]("about"){
def id = column[Long]("id", O.PrimaryKey, O AutoInc)
def name = column[String]("name")
def subheading = column[String]("subheading")
def about = column[String]("about")
def * = id.? ~ name ~ subheading ~ about <> (About.apply _, About.unapply _)
def update(id: Long, about: About)(implicit session: Session) = findById(id).update(about)
def findById(pk: Long) =
for (a <- About if a.id === pk) yield a
}

Replace your findById with:
def findById(pk: Long) =
for (a <- Query(About) if a.id === pk) yield a
Maybe you will have to add import simple._ under the driver import.

Also, you can use this method too:
def findById(id: Int) = {
byId(id).list.headOption
}
and import scala.slick.jdbc.{ GetResult, StaticQuery }

Related

playframework scala slick insert query without autoincrement

Good Morning,
I've got the following code snippet:
def createResponsibleProcessTemplates(processTemplateId: Int, username: String): Future[Int] = db.run {
println("Create Responsible TemplateId: " + processTemplateId + " UserName: " + username)
(responsibleProcessTemplates
returning responsibleProcessTemplates.map(_.processTemplateId)
) += ResponsibleProcessTemplateModel(processTemplateId, username)
}
My Model is:
package models
import play.api.libs.json.Json
case class ResponsibleProcessTemplateModel(
processTemplateId: Int,
username: String)
object ResponsibleProcessTemplateModel {
implicit val responsibleProcessTemplateFormat = Json.format[ResponsibleProcessTemplateModel]
}
And the trait looks like this:
trait ResponsibleProcessTemplateComponent {
self: HasDatabaseConfigProvider[JdbcProfile] =>
import profile.api._
class ResponsibleProcessTemplate(tag: Tag) extends Table[ResponsibleProcessTemplateModel](tag, "Responsible_ProcessTemplates") {
def processTemplateId: Rep[Int] = column[Int]("processTemplateId")
def username: Rep[String] = column[String]("username")
def * : ProvenShape[ResponsibleProcessTemplateModel] = (processTemplateId, username) <> ((ResponsibleProcessTemplateModel.apply _).tupled, ResponsibleProcessTemplateModel.unapply)
}
val responsibleProcessTemplates: TableQuery[ResponsibleProcessTemplate] = TableQuery[ResponsibleProcessTemplate]
}
It should insert some data into a database table without generating an autoincerement id.
I don't get errors, but data is not stored in database.
Thanks for your help.
I found the solution!
def createResponsibleProcessTemplates(processTemplateId: Int, username: String): Future[Int] = {
db.run(responsibleProcessTemplates.map(x => (x.processTemplateId, x.username)) += (processTemplateId, username))
}

How to use Filter to get Boolean - Scala Slick

I have AccountTable:
import models.{Account, Category}
import play.api.Play
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfig}
import slick.driver.JdbcProfile
import slick.driver.PostgresDriver.api._
import slick.lifted.Tag
import play.api.libs.json.{JsValue, Writes, Json}
object AccountTable extends AccountTable
trait AccountTable extends HasDatabaseConfig[JdbcProfile]{
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
class Accounts(tag: Tag) extends Table[Account](tag, "account") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def login = column[String]("login")
def password = column[String]("password")
def * = (id, login, password) <> ((Account.apply _).tupled, Account.unapply)
}
val accounts = TableQuery[Accounts]
implicit val accountFormat = Json.format[Account]
def auth(login: String, password: String): Boolean = {
db.run(findByLogin(login, password).result.headOption)
}
private def findByLogin(login: String, password: String) = accounts.filter(x => (x.password === password && x.login === login))
}
I try to develop auth method. I really don't understand, how to complete this method. I tried different ways, but always I get different errors.
Option 1:
def auth(login: String, password: String): Future[Boolean] = {
val action = findByLogin(login, password).result.headOption
.map(authOpt => authOpt.getOrElse(false))
db.run(action)
}
Option 2:
def auth(login: String, password: String): Boolean = {
val action = findByLogin(login, password).result.headOption
.map(authOpt => authOpt.getOrElse(false))
Await.result(db.run(action), 5 seconds)
}
The code above is untested, but you should get the idea behind queries in slick. I assumed that auth should return false if no result was found in the db.
One last word: I strongly recommend to use option 1 and work with Future, as Await.result blocks the thread from execution.

how to get the Joined foregin key object in slick?

I use playframework2+slick2.0
My datamodel:
class Page(tag:Tag) extends Table[(Int,Int, String,String,String, Option[String], Option[String])](tag, "Page"){
def id=column[Int]("ID", O.PrimaryKey)
def subId=column[Int]("subject")
def title=column[String]("Title", O.NotNull)
def describe=column[String]("Describe")
def profile=column[String]("Profile")
def icon=column[Option[String]]("icon")
def resId=column[String]("Picture")
def * = (id, subId,title, describe, profile,icon, resId.?)
def page_sub=foreignKey("PA_SU_FK", subId, subject)(_.id)
def page_res=foreignKey("PA_RE_FK", resId, resource)(_.link)
}
val page=TableQuery[Page]
class Resource(tag:Tag) extends Table[(String, String, Boolean)](tag, "Resource"){
def link=column[String]("Link", O.PrimaryKey)
def rtype=column[String]("class")
def local=column[Boolean]("local")
def * = (link, rtype,local)
}
val resource=TableQuery[Resource]
I want to use the filter to get one row, and get the resource object form page:
my code is like:
val item=page.filter(_.id===id.toInt).first()(rs.dbSession)
This is only get the page real object, I want get the correspond resource object, how to get it by filter?
You can use for-comprehension:
val pageWithResouce = (for {
p <- page if (p.id === id.toInt)
r <- resource if (p.resId === r.link)
} yield { (p, r) })list

missing OptionMapper for join on custom mapped id types in slick 1.0

TL;DR: Why doesnt this compile? https://github.com/refried/typedids-option-fk-issue
I'm using Slick to access an Oracle DB. I've tried to implement custom ID types (e.g. UserID, AgencyID) for safer queries, in the style of cvogt's scaladays2013 example. But when I attempt to do a join for a FK:
package controllers
import model._
import scala.slick.session.Database.threadLocalSession
import com.typesafe.slick.driver.oracle.OracleDriver.simple._
object Auth {
def auth(username: String, password: String): Boolean = {
val q = for {
(u,s) <- User leftJoin Agency on (_.agencyId is _.id)
if u.name.toLowerCase === username.toLowerCase.trim && u.password === fn_hash(password)
} yield (s.dataOutOfDate.?.getOrElse(false))
db withSession {
q.list.map {
case (agencyOutOfDate) => !agencyOutOfDate
}.headOption.getOrElse(false)
}
}
}
I get an error I don't know how to resolve.
Auth.scala:11: Cannot perform option-mapped operation
[error] with type: (Option[model.AgencyId], model.AgencyId) => R
[error] for base type: (model.AgencyId, model.AgencyId) => Boolean
[error] (u,s) <- User leftJoin Agency on (_.agencyId is _.id)
I didn't have any problem when I was using the same mapped ID type for all my columns:
case class ID(bytes: Array[Byte]) extends AnyVal
object ID {
implicit val typemapper = MappedTypeMapper.base[ID,Array[Byte]](_.bytes, ID.apply)
}
Although today, I am having trouble even reproducing / compiling that. What am I missing? Here's my model source:
import play.api.db.DB
import play.api.Play.current // implicit Application
import scala.slick.lifted.MappedTypeMapper
import com.typesafe.slick.driver.oracle.OracleDriver.simple._
package object model {
def db = Database.forDataSource(DB.getDataSource())
/* Custom ID types */
type RawID = Array[Byte]
trait TypedId extends Any {
def untypedId: RawID
override def toString = untypedId.map("%02x" format _).mkString
}
case class AgencyId(untypedId: RawID) extends AnyVal with TypedId
case class UserId(untypedId: RawID) extends AnyVal with TypedId
/* Custom ID type mappers */
sealed trait IdFactory[T <: TypedId] extends (RawID => T)
implicit object AgencyId extends IdFactory[AgencyId]
implicit object UserId extends IdFactory[UserId]
implicit def idMapper[T <: TypedId : IdFactory]: TypeMapper[T] =
MappedTypeMapper.base[T, RawID](_.untypedId, implicitly[IdFactory[T]])
/* Tables & Queries */
case class User(id: Option[UserId] = None,
name: String,
password: String,
agencyId: Option[AgencyId] = None,
inactive: Boolean = false,
disabled: Boolean = true)
object User extends Table[User]("USER") {
def id = column[UserId]("ID", O.PrimaryKey)
def name = column[String]("NAME")
def password = column[String]("PASSWORD")
def agencyId = column[Option[AgencyId]]("AGENCY_ID")//(typeMapperToOptionTypeMapper(idMapper(AgencyId)))
def inactive = column[Boolean]("INACTIVE", O.Default(false))
def disabled = column[Boolean]("IS_DISABLED", O.Default(true))
def * = id.? ~ name ~ password ~ agencyId ~ inactive ~ disabled <> (User.apply _, User.unapply _)
def agency = foreignKey("FK_TB_USER_AGENCY_ID",agencyId,Agency)(_.id.?)
}
case class Agency(id: Option[AgencyId] = None, name: String, dataOutOfDate: Boolean = false)
object Agency extends Table[Agency]("AGENCY") {
def id = column[AgencyId]("ID", O.PrimaryKey)
def name = column[String]("NAME")
def dataOutOfDate = column[Boolean]("IS_DATA_OUT_OF_DATE")
def * = id.? ~ name ~ dataOutOfDate <> (Agency.apply _, Agency.unapply _)
}
/* Remote functions */
val fn_hash = SimpleFunction.unary[String,String]("fn_hash")
}
My extra Play application dependencies, although I don't think my issue is Play-specific:
"com.typesafe.slick" %% "slick-extensions" % "1.0.0",
"com.typesafe.play" %% "play-slick" % "0.3.3"
Many thanks in advance!
Evidently idMapper only needed to have type BaseTypeMapper[T] rather than TypeMapper[T].

SLICK How to define bidirectional one-to-many relationship for use in case class

I am using SLICK 1.0.0-RC2. I have defined the following two tables Directorate and ServiceArea where Directorate has a one to many relationship with ServiceArea
case class Directorate(dirCode: String, name: String)
object Directorates extends Table[Directorate]("DIRECTORATES") {
def dirCode = column[String]("DIRECTORATE_CODE", O.PrimaryKey)
def name = column[String]("NAME")
def * = dirCode ~ name <> (Directorate, Directorate.unapply _)
}
case class ServiceArea(areaCode: String, dirCode: String, name: String)
object ServiceAreas extends Table[ServiceArea]("SERVICE_AREAS") {
def areaCode = column[String]("AREAE_CODE", O.PrimaryKey)
def dirCode = column[String]("DIRECTORATE_CODE")
def name = column[String]("NAME")
def directorate = foreignKey("DIR_FK", dirCode, Directorates)(_.dirCode)
def * = areaCode ~ dirCode ~ name <> (ServiceArea, ServiceArea.unapply _)
}
To make the Directorate case class useful in my Play application form I am trying to redefine the Directorate case class to have a Seq of ServiceAreas that are related to that Directorate.
case class Directorate(dirCode: String, name: String, serviceAreas: Seq[ServiceArea])
My problem is now with the Directorate table projection. I have attempted to create a method in Directorates:
def serviceAreas = (for { a <- ServiceAreas
if (a.dirCode === dirCode)
} yield (a)).list map {
case t: ServiceArea => t
}
so that I can try something like
def * = dirCode ~ name ~ serviceAreas <> (Directorate, Directorate.unapply _)
but this cannot not work as serviceAreas only goes one way.
It seems reasonable to me that for the Directorate case class to be a useful domain object that it should be able contain the related ServiceAreas.
I'm wondering how I should traverse the inverse relationship so that Directorate table projection will work.
I'm sure there is a more elegant solution, but this should do the trick:
import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession
object SlickExperiments2 {
Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
(Directorates.ddl ++ ServiceAreas.ddl).create
case class Directorate(dirCode: String, name: String) {
def serviceAreas: Seq[ServiceArea] = (for {
a <- ServiceAreas
if (a.dirCode === dirCode)
} yield (a)).list
}
object Directorates extends Table[Directorate]("DIRECTORATES") {
def dirCode = column[String]("DIRECTORATE_CODE", O.PrimaryKey)
def name = column[String]("NAME")
def * = dirCode ~ name <> (Directorate, Directorate.unapply _)
}
case class ServiceArea(areaCode: String, dirCode: String, name: String)
object ServiceAreas extends Table[ServiceArea]("SERVICE_AREAS") {
def areaCode = column[String]("AREAE_CODE", O.PrimaryKey)
def dirCode = column[String]("DIRECTORATE_CODE")
def name = column[String]("NAME")
def directorate = foreignKey("DIR_FK", dirCode, Directorates)(_.dirCode)
def * = areaCode ~ dirCode ~ name <> (ServiceArea, ServiceArea.unapply _)
}
Directorates.insert(Directorate("Dircode", "Dirname"))
ServiceAreas.insertAll(ServiceArea("a", "Dircode", "A"), ServiceArea("b", "Dircode", "B"))
val sa = (for{
d <- Directorates
} yield d).list map { case t: Directorate => t.serviceAreas}
println(sa)
}
//> List(List(ServiceArea(a,Dircode,A), ServiceArea(b,Dircode,B)))
}