Multiple values selection in <g:select> error - grails

I'm trying to use multiple values for a select using <g:select multiple='true'>, but when i try to save the form to the DB, i got this error
Property [Languages] of class [class com.Myapp.hr.EmploymentSeeker] with value [french,english] is not contained within the list [[french, english, russian, chinese]]
here is my Domain:
class EmploymentSeeker {
Set<String> languages = [] as Set
static hasMany = [ languages: String ]
static constraints = {
languages(nullable:true,inList:Holders.config.languages)
}
}
Config file :
languages=[
'french',
'english',
'russian',
'chinese'
]
GSP:
<g:select multiple="true" name="languages" from="${employmentSeekerInstance.constraints.languages.inList}" value="${employmentSeekerInstance?.languages}" valueMessagePrefix="empSeeker.languages" noSelection="['': '']"/>
what may cause this error ?

As mentioned in Grails doc:
inList: Constrains a value so that it must be contained within the given list.
The problem is that your inList constrant it's a list of Strings, but languages from EmploymentSeeker isn't a String, it's a Set.
If it was a String, it would work, for example:
'french' is contained in ['french','english','russian','chinese']
But you have a Set, so
['french'] isn't contained in ['french','english','russian','chinese']
To make it work with inList you have to set in the constraint list all the combinations that user could choose, like:
[['french'], ['french', 'english'], ['english', 'french'] ...] and so on.
This answer describes how to archive those combinations, but actually it's not a good solution, because subsequences() doesn't care about permutations. However, adding permutations will increase your constraint list exponentially after growing of languages list.
As you can see, using inList is not a good solution in your case.
I would recomment to implement your custom simple constraint, like this:
languages(nullable:true,validator: {value, object ->
Holders.config.languages.containsAll(value)​​​)​
})
You can read about custom constraints here and here

Related

Determine Data Type

I have this code in my controller:
def cols = grailsApplication.getDomainClass('com.archie.Build').persistentProperties.collect {it.name}
The code above will allow me to list all the property names I have in Build class. Now, I would like to include also the properties data type, ie. boolean, String etc...
Somewhat like the output is:
[floorType:String, floorWidth:Float, ......]
Maybe not exactly like that, or maybe similar, but as long as I can return their data type. Can someone help? Thank you.
Each entry in persistentProperties is a GrailsDomainClassProperty, and this provides access to the type of the property as a Class object:
def props = [:]
grailsApplication.getDomainClass('com.archie.Build'
).persistentProperties.each {
props[it.name] = it.type.name
}
Or just pass the persistentProperties array itself through to the GSP, then extract .name and .type there.
You may also wish to consider using constrainedProperties instead of/in addition to the persistentProperties. The constrainedProperties map lists only those properties that are mentioned in the domain class constraints block, but the iterator over this map is guaranteed to return the properties in the order they are listed in the constraints. This is how the default scaffolding operates, as I'm not aware of any way to control the order of the persistentProperties array.

GORM/Grails :: possible to query based on contents of a List within the model?

Assume the following:
class Thing {
String name
List<String> tags
static constraints = {
name(nullable: false)
tags(nullable: false)
}
}
I want to know if its possible, using GORM, to run a query for domain instances based on values in their respective lists
For instance: Are there dynamic GORM finders to query things like 'Find all Things that have the tag "Video" ', or 'Find all things with name = "Product1" that have the tag "Image" '
Just want to know if there's a nice concise way of doing this with Grails&Gorm, as opposed to retrieving a list of Things and iterating through it, finding the ones that have the appropriate tags and adding them to a results list.
Thanks!
One way (although not necessarily the most efficient!) would be to return the whole list of Things eg Thing.list() and then filter the resulting list using findAll.
List results = Thing.list().findAll{it.tags.contains("Image")}
How big is your list of Things and associated Tags likely to be?

GORM mapping: make an index unique

I'm feeling a little slow today. I'm trying to do something that I think is very simple. I have a Domain class with a property called 'name'. I want 'name' to have an index, and I want the index to require that the 'name' is unique. I've set the unique constraint and tried creating an index. I can't make sense out of the Gorm docs as to how I add the unique attribute to the index. Here's some code:
class Project {
String name
static hasMany = [things:Things]
static mapping = {
name index:'name_idx'
}
static constraints = {
name(unique:true)
}
}
All is well with the above, except when do "show indexes from project" in mysql it shows my name key as not unique. I know the problem is that I am not specifying unique in the mapping, but quite frankly the docs for gorm are making my head hurt. I see all kinds of stuff about columns, but I can't find a single example anywhere on the web that shows what I want to do. I don't need complex mappings or compound keys, I just want to know the syntax to add the unique attribute to the mapping declaration above. Any advice welcome.
I also did a grails export-schema and see the following:
create index name_idx on project (name);
Nothing in that to indicate this index requires unique values
A related followup question would be once I succeed in making that index unique, what type of error should I expect when I go to save a Project instance and the name is not unique? Is there a specific exception thrown? I realize that even if I check that a given 'name' is unique there's still a possibility that by the time I save it there may be a row with that name.
I'm quite sure the syntax to do what I want is simple but I just can't find a simple example to educate myself with. I've been to this page but it doesn't explain HOW the uniqueness is enforced. I'd like to enforce it at the name index level.
The indexColumn allows additional options to be configured. This may be what you're looking for.
static mapping = {
name indexColumn:[name:'name_idx', unique:true]
}
Grails Documentation for indexColumn
If you put only the unique constraint the GORM send DDL to create an unique index on database.
static constraints = {
name nullable: false, unique: true
}

Language-specific value in grails inList constraint

Users of my application have the possibility of choosing some values from list. The values for that list are in simple domain class, Foo, which looks like that:
class Foo{
String name
static mapping = {
id name: 'name', generator: 'assigned'
version: false
}
}
Foo looks the same for every language my app uses. In another class I have a constraint saying that Bar must be in list of Foo. Sometimes user doesn't know what to choose, so he may choose something like "I'm not sure" (so this option should be in list to to meet the inList constraint). Thing is, "I'm not sure" is written differently in different languages. How can I append this value based on current messages to inList constraint?
In your controller you could do:
def theList = foo.list().name // Get any array of strings.
// If you actually need > 1 field then you probably need to
// put the g.message below in a map
theList << g.message(code:"im.not.sure")
I don't believe inList constraint will help you here - it's designed for a simpler use case than yours.
I'd add a method to the class getLanguages() that handles this, and then since you seem to be interested in validation, write a custom validator to make sure right values are saved.

Mapping hasMany = [field:String] to a database field TEXT

Is it possible to set the database column type of a hasMany association to a primitive type (in my case string) to a specific value? I know how to set a field (see this question), but this is different.
List textRecords
static hasMany = [
textRecords:String,
]
I want to make sure that my textRecords are mapped to a TEXT or LONGTEXT database type.
I could create a separate domain class that contains just one string field and map that field, but that seems like a kluge.
The Grails User Guide has an example listed in 5.2.1.4 Basic Collection Types section that is very similar to what you want to accomplish.
Keep in mind that every time you want to add a textRecord to that object it will have to load the entire List of textRecords in order to save it again. This may not be the behaviour that you want for performance reasons.
You'll end up having a mappings block like this:
static mapping = {
hasMany joinTable:[name:'bunch_o_text_records', key:'domain_id', column:'text_record', type:"text"]
}

Resources