I use play-slick, and I tried to do exactly the same as you, with a tag and using self-type without success.
But I succeeded:
import modelsunscanned.TableWithId import scala.slick.jdbc.JdbcBackend import scala.slick.lifted.TableQuery import play.api.db.slick.Config.driver.simple._ /** * @author Sebastien Lorber ( lorber.sebastien@gmail.com ) */ package object models { private[models] val Users = TableQuery(new UserTable(_)) private[models] val Profiles = TableQuery(new ProfileTable(_)) private[models] val Companies = TableQuery(new CompanyTable(_)) private[models] val Contacts = TableQuery(new ContactTable(_)) trait ModelWithId { val id: String } trait BaseRepository[T <: ModelWithId] { def tableQuery: TableQuery[TableWithId[T]] private val FindByIdQuery = Compiled { id: Column[String] => tableQuery.filter(_.id === id) } def insert(t: T)(implicit session: JdbcBackend#Session) = { tableQuery.insert(t) } def getById(id: String)(implicit session: JdbcBackend#Session): T = FindByIdQuery(id).run.headOption .getOrElse(throw new RuntimeException(s"Could not find entity with id=$id")) def findById(id: String)(implicit session: JdbcBackend#Session): Option[T] = FindByIdQuery(id).run.headOption def update(t: T)(implicit session: JdbcBackend#Session): Unit = { val nbUpdated = tableQuery.filter(_.id === t.id).update(t) require(nbUpdated == 1,s"Exactly one should have been updated, not $nbUpdated") } def delete(t: T)(implicit session: JdbcBackend#Session) = { val nbDeleted = tableQuery.filter(_.id === t.id).delete require(nbDeleted == 1,s"Exactly one should have been deleted, not $nbDeleted") } def getAll(implicit session: JdbcBackend#Session): List[T] = tableQuery.list } } // play-slick bug, see https://github.com/playframework/play-slick/issues/227 package modelsunscanned { abstract class TableWithId[T](tableTag: Tag,tableName: String) extends Table[T](tableTag,tableName) { def id: Column[String] } }
I give you an example of use:
object CompanyRepository extends BaseRepository[Company] { // Don't know yet how to avoid that cast :( def tableQuery = Companies.asInstanceOf[TableQuery[TableWithId[Company]]] // Other methods here ... } case class Company( id: String = java.util.UUID.randomUUID().toString, name: String, mainContactId: String, logoUrl: Option[String], activityDescription: Option[String], context: Option[String], employeesCount: Option[Int] ) extends ModelWithId class CompanyTable(tag: Tag) extends TableWithId[Company](tag,"COMPANY") { override def id = column[String]("id", O.PrimaryKey) def name = column[String]("name", O.NotNull) def mainContactId = column[String]("main_contact_id", O.NotNull) def logoUrl = column[Option[String]]("logo_url", O.Nullable) def activityDescription = column[Option[String]]("description", O.Nullable) def context = column[Option[String]]("context", O.Nullable) def employeesCount = column[Option[Int]]("employees_count", O.Nullable) // def * = (id, name, mainContactId,logoUrl, activityDescription, context, employeesCount) <> (Company.tupled,Company.unapply) // def name_index = index("idx_name", name, unique = true) }
Please note that active-slick also uses something similar.