我遇到了迭代传递给Play框架模板的List的问题。我本质上有一个从多对多关联中获取的查询,我想要多次呈现父键和关联键。
以下是我正在使用的实际代码:
使用Slick,Scala和Play 2.0,我有以下表格架构:
object Recipes extends Table[(Long, String, String)]("RECIPES") {
def id = column[Long]("REC_ID", O.PrimaryKey, O.AutoInc)
def cuisine = column[String]("CUISINE")
def instructions = column[String]("INSTRUCTIONS")
def * = id ~ cuisine ~ instructions
}
object Ingredients extends Table[(Long, String, String)]("INGREDIENTS") {
def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
def brand = column[String]("BRAND")
def name = column[String]("NAME")
def * = id ~ brand ~ name
}
object RecipeIngredient extends Table[(Long, Long, Long, Int, String)]("REC_ING") {
def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
def recID = column[Long]("REC_ID")
def ingID = column[Long]("ING_ID")
def quantity = column[Int]("QUANTITY")
def units = column[String]("UNITS")
def * = id ~ recID ~ ingID ~ quantity ~ units
def recipe = foreignKey("REC_FK", recID, Recipes)(_.id)
def ingredient = foreignKey("ING_FK", ingID, Ingredients)(_.id)
}
我正在使用Slick在控制器中生成以下查询,并将q.list
传递给视图。我们的想法是通过ID 1传递和渲染配方以及所有相关成分:
val recID = 1.longValue() // Just a test to get the Recipe with ID === 1
val q = for {
r <- Recipes if r.id === recID
ri <- RecipeIngredient if ri.recID === recID
i <-Ingredients if i.id === ri.ingID
} yield (r.id, r.cuisine, r.instructions, ri.quantity, ri.units, i.brand, i.name)
我的观点如下:
@(message: String, result: List[(Long, String, String, Int, String, String, String)])
@main("Site name") {
@for((id, cuisine,instructions, quantity, units, brand, name) <- result) {
<h2>--Recipe--</h2>
RecID: @id <br>
Cuisine: @cuisine <br>
Instructions: @instructions <br>
<h2>--Ingredients--</h2>
Ingredient: @quantity @units of @brand @name<br>
}
}
这一切都很好,但我得到如下输出:
--Recipe--
RecID: 1
Cuisine: Chinese
Instructions: Instructions here..
--Ingredients--
Ingredient: 3 cloves of Generic Ginger
--Recipe--
RecID: 1
Cuisine: Chinese
Instructions: Instructions here..
--Ingredients--
Ingredient: 3 slices of Generic Cucumber
如您所见,食谱本身重复两次。我最终想要的是食谱印刷一次,相关成分列表在此之后进行交互和显示(可能有多种成分)。
关于如何实现这一点的任何想法?
答案 0 :(得分:1)
就最佳实践/更优雅的方式而言,您应该考虑创建一个食谱案例类来保存食谱的所有信息;这将使您的代码更清晰,更易于使用:
case class Recipe(val id: Long, val cuisine: String, val instructions: String, val quantity: Int, val units: String, val brand: String, val name: String)
注意:当我访问视图中的字段时,所有字段都明确标记为val,以便于使用。 然后,您可以将查询转换为对象(来自scala slick query return value)
def getRecipe(recID: Long): Option[Recipe] = {
val q = for {
r <- Recipes if r.id === recID
ri <- RecipeIngredient if ri.recID === recID
i <-Ingredients if i.id === ri.ingID
} yield (r.id, r.cuisine, r.instructions, ri.quantity, ri.units, i.brand, i.name)
q.firstOption map { case (id, cui, ins, qua, uni, bra, na) => Recipe(id, cui, ins, qua, uni, bra, na) }
}
然后您可以将其传递给您的观点:
@(message: String, recipe: Recipe)
@main("Site name") {
@recipe match {
case r:Some(Recipe) => {
<h2>--Recipe--</h2>
RecID: @r.id <br>
Cuisine: @r.cuisine <br>
Instructions: @r.instructions <br>
<h2>--Ingredients--</h2>
Ingredient: @r.quantity @r.units of @r.brand @r.name<br>
}
case None => {
<h2>No Recipe</h2>
}
}
}
你可以做一些不同的事情,比如为Recipe案例类创建一个伴随对象类,摆脱传递给你视图的Option [Recipe]等等。 如果您想要选择多个配方并将它们在List [Recipe]中传递给您可以迭代的视图,这也会更容易。
希望这有帮助。
答案 1 :(得分:0)
我找到了解决这个问题的方法,虽然看起来非常黑,所以我仍然热衷于理解最佳实践,优雅的方式来做到这一点。
我的解决方案 - 通过将视图更改为:
@main("Site name") {
// This is a hacky way to just show the Recipe components once
@for((item, index) <- result.zipWithIndex) {
@if(index == 0) {
<h2>---Recipe---</h2>
RecID: @item._1 <br>
Cuisine: @item._2<br>
Instructions: @item._3 <br>
<h2>---Ingredients---</h2>
}
}
// And now we list all ingredients..
@for((id, cuisine,instructions, quantity, units, brand, name) <- result) {
<!--<h2>Recipe</h2>
RecID: @id <br>
Cuisine: @cuisine <br>
Instructions: @instructions <br>-->
Ingredient: @quantity @units of @brand @name<br>
}
}
..我得到了我想要的输出:
---Recipe---
RecID: 1
Cuisine: Chinese
Instructions: Instructions here
---Ingredients---
Ingredient: 3 cloves of Generic Ginger
Ingredient: 3 slices of Generic Cucumber
当然,有一种更易读的方法可以做到这一点吗?