重复值的唯一约束

时间:2012-11-17 16:07:33

标签: forms scala playframework playframework-2.0 unique-constraint

我正在define a form尝试使用以下属性和约束play! 2.0.4

  1. 表单处理重复值(可以方便地假设这些值的类型为number)。所以这将使我们得到这样的东西:

    "numbers" -> list(number)
    
  2. 每个数字必须是唯一的,即它对于提交的所有其他数字必须是唯一的,并且它必须对数据库中已存在的数字是唯一的(这可以通过某个函数{{ 1}})。

  3. 表单错误应该特定于数字,这不是唯一的。我不希望一般表单错误说“有重复的数字”。

  4. 最好的方法是什么?

2 个答案:

答案 0 :(得分:3)

这里的诀窍是定义一个类似example的自定义Constraint。然后,可以在Constraint上使用自定义Mapping[T],以使用verifying方法验证表单中的字段。

自定义Constraint包含返回ValidationResultValid Invalid的逻辑。可以将错误消息传递给Invalid结果,您可以在该结果中指定数据库中存在重复或存在的内容。

有关自定义验证的部分,请参阅Play for Scala

- 创建约束

  //Make this lazy to prevent java.lang.ExceptionInInitializerError at runtime.
  lazy val uniqueNumbersConstraint = Constraint[String](Some("Unique numbers constraint"), "")(checkNumbers)

  //"Business Logic". 
  //Important part here is that the function returns a ValidationResult and complies with the signature for Constraint. i.e. f: (T) => ValidationResult
  //Return Valid if n in numbers is not in database and there are no duplicates.   
  //Otherwise return Invalid and an error message showing what numbers are in the database or duplicated. 
  def checkNumbers(numbers: String):ValidationResult  = {
    val splitNums = numbers.split(" ").toList.map(_.toInt)
    val dbnums  = splitNums.partition(database.contains(_))
    if(dbnums._1.isEmpty && uniquesAndDuplicates(splitNums)._2.isEmpty){
      Valid
    }else{
      val duplicates = uniquesAndDuplicates(dbnums._2)._2
      val error = "Database contains: " + dbnums._1 + ", duplicated values: " + duplicates
      Invalid(error)
    }
  }

- 使用自定义约束验证表单

  val helloForm = Form(
    tuple(
      "numbers" -> nonEmptyText.verifying(uniqueNumbersConstraint)
    ))

- 实用工具

  //Return unique values on left side and duplicate values on right side
  def uniquesAndDuplicates(numbers: List[Int]):Tuple2[List[Int], List[Int]] = {
    numbers.partition(i => numbers.indexOf (i) == numbers.lastIndexOf(i))
  }

  def checkNum(num: Int) = {
    database.contains(num) 
  }

  val database = List(5,6,7)

- 等

注意我在表单中将numbers定义为String。当我将其定义为list(number)时,它会继续评估为List()。我认为这是一个具有约束力的问题。如果使用List(1,2,3)有效,则使用"1 2 3"而不是list(number)进行相当简单的更改。

- 样本

enter image description here enter image description here enter image description here

答案 1 :(得分:2)

如下:

def validateUnique(input: List[Int]): ValidationResult = {

  // Assuming check return true if the input num doesn't exist yet
  def check(num: Int): Boolean = num % 2 == 0
  val unique = input.toSet
  val dbDuplicates = unique.filterNot(check)
  val formDuplicates = input.diff(unique.toSeq)
  val duplicates = (dbDuplicates ++ formDuplicates).toList

  duplicates match {
    case List() => Valid
    case _ => Invalid("Duplicates: " + duplicates.mkString(", "))
  }
}

val uniqueConstraint = Constraint[List[Int]](validateUnique(_))

然后您可以使用新约束:

mapping(
  ...,
  "ints" -> list(number).verifying(uniqueConstraint)
  ...