Saving a many-to-many object in rake

I am trying to save many-many related objects. A SellingCompany can have many accounts, and an account can be associated with many SellingCompanies. Thus, there are many relationships between tables stored in SellingCompaniesAccount

My Account_Info domain is as follows:

class AccountInfo { static mapping ={ table 'AccountInfo' version false //id column:'accountInfoID' } String evi_pass_phrase String evi_username String security_key // to make sure fields show up in a particular order static constraints = { //accountInfoID(insert:false,update:false) evi_pass_phrase() evi_username() security_key() } static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount] String toString() { return "${evi_username}" } } 

My SellingComapanies domain is as follows:

 class SellingCompanies { static mapping = { table 'SellingCompanies' version false } String name //static belongsTo = AccountInfo //static hasMany = [accounts: AccountInfo] static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount] static constraints = { name(blank:false, validator: { val, obj -> def similarSellingCompanies = SellingCompanies.findByNameIlike(val) return !similarSellingCompanies || (obj.id == similarSellingCompanies.id) }) } //String toString() { name } } 

The table that contains the Many-Many relationships is as follows:

 class SellingCompaniesAccount { static constraints = { // ensure the group of sellingCompaneis and accountInfo values are unique agency_name(unique:['sellingCompanies','accountInfo']) } int agency_id String agency_name String consultant_id String code Boolean isActive String iata ContactInfo contactinfo static belongsTo = [sellingCompanies:SellingCompanies, accountInfo:AccountInfo] } } 

The form in the create.gsp file contains code that actually iterates through all the different SellingCompanies and displays as a flag.

 <g:form action="save" method="post"> <div class="dialog"> <table width="500px" border="0px" color="red"> <tbody> <tr class="prop"> <td valign="top" class="name"><label for="accountInfo"><g:message code="sellingCompaniesAccount.accountInfo.label" default="Account Info" /></label></td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'accountInfo', 'errors')}"> <g:select name="accountInfo.id" from="${content_hub_admin.AccountInfo.list()}" optionKey="id" value="${sellingCompaniesAccountInstance?.accountInfo?.id}" /></td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="sellingCompanies"><g:message code="sellingCompaniesAccount.sellingCompanies.label" default="Selling Companies" /></label></td> <td valign="top" class=""> <g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i"> ${++i}. ${item.name}&nbsp;&nbsp;<g:checkBox name="sellingcompanies_${++i-1}" optionKey="id" value="${item.id}" /> <br> </g:each> <!-- end here by rsheyeah --> </td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="code"><g:message code="sellingCompaniesAccount.code.label" default="Code" /></label></td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'code', 'errors')}"> <g:textField name="code" value="${sellingCompaniesAccountInstance?.code}" /></td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="agency_name"><g:message code="sellingCompaniesAccount.agency_name.label" default="Agencyname" /></label></td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_name', 'errors')}"> <g:textField name="agency_name" value="${sellingCompaniesAccountInstance?.agency_name}" /></td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="isActive"><g:message code="sellingCompaniesAccount.isActive.label" default="Is Active" /></label> </td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'isActive', 'errors')}"> <g:checkBox name="isActive" value="${sellingCompaniesAccountInstance?.isActive}" /></td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="agency_id"><g:message code="sellingCompaniesAccount.agency_id.label" default="Agencyid" /></label> </td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_id', 'errors')}"> <g:textField name="agency_id" value="${fieldValue(bean: sellingCompaniesAccountInstance, field: 'agency_id')}" /> </td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="iata"><g:message code="sellingCompaniesAccount.iata.label" default="Iata" /></label></td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'iata', 'errors')}"> <g:textField name="iata" value="${sellingCompaniesAccountInstance?.iata}" /></td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="consultant_id"><g:message code="sellingCompaniesAccount.consultant_id.label" default="Consultantid" /></label></td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'consultant_id', 'errors')}"> <g:textField name="consultant_id" value="${sellingCompaniesAccountInstance?.consultant_id}" /></td> </tr> <tr class="prop"> <td valign="top" class="name"><label for="contactinfo"><g:message code="sellingCompaniesAccount.contactinfo.label" default="Contactinfo" /></label></td> <td valign="top" class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'contactinfo', 'errors')}"> <g:select name="contactinfo.id" from="${content_hub_admin.ContactInfo.list()}" optionKey="id" value="${sellingCompaniesAccountInstance?.contactinfo?.id}" /></td> </tr> </tbody> </table> </div> <div class="buttons"><span class="button"><g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" /></span> </div> </g:form> 

Finally, a controller that handles the save and list functions.

 class SellingCompaniesAccountController { private static Logger log = Logger.getLogger(SellingCompaniesAccountController.class) //def index = { } //def scaffold = true def index = { redirect(action:list,params:params) } //To limit access to controller actions based on the HTTP request method. def allowedMethods = [save:'POST'] //create.gsp exists def create = { render(view:"create") } //edit.gsp exists //def edit = {} //list.gsp exists def list = { [ sellingCompaniesAccountInstanceList: SellingCompaniesAccount.list( max:15) ] } //show.gsp exists //def show={} //save.gsp exists def save = { log.info "Saving: " + params.toString() println("Saving: " + params.toString()) def sellingCompaniesAccount = params.sellingCompaniesAccount println(sellingCompaniesAccount) def sellingCompanies = params.sellingCompanies log.info "sellingCompanies: " + sellingCompanies println(sellingCompanies) def sellingCompaniesAccountInstance = new SellingCompaniesAccount(name: params.name) println(params.name) params.each { if (it.key.contains("_sellingcompanies")) //sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer) if (it.key.contains("sellingcompanies_")) sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer) } log.info sellingCompaniesAccountInstance if (sellingCompaniesAccountInstance.save(flush: true)) { flash.message = "${message(code: 'default.created.message', args: [message(code: 'sellingCompaniesAccountInstance.label', default: 'sellingCompaniesAccountInstance'), sellingCompaniesAccountInstance.id])}" redirect(action: "show", id: sellingCompaniesAccountInstance.id) log.info sellingCompaniesAccountInstance } else { render(view: "create", model: [sellingCompaniesAccountInstance: sellingCompaniesAccountInstance]) } } } 

Now I get the following error due to empty hidden values ​​similar to _sellingcompanies_1, etc .:

Error Logs:

 Saving: ["accountInfo.id":"1", "accountInfo":["id":"1"], "_sellingcompanies_5":"", "_isActive":"", "code":"test", "agency_name":"test", "sellingcompanies_4":"4", "sellingcompanies_5":"5", "create":"Create", "isActive":"on", "iata":"test", "agency_id":"test", "contactinfo.id":"1", "contactinfo":["id":"1"], "consultant_id":"test", "sellingcompanies_2":"2", "_sellingcompanies_1":"", "sellingcompanies_3":"3", "_sellingcompanies_2":"", "_sellingcompanies_3":"", "sellingcompanies_1":"1", "_sellingcompanies_4":"", "action":"save", "controller":"sellingCompaniesAccount"] null null null 2011-03-15 17:13:44,620 [http-8080-2] ERROR org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver - For input string: "_5" java.lang.NumberFormatException: For input string: "_5" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:449) at java.lang.Integer.valueOf(Integer.java:554) at content_hub_admin.SellingCompaniesAccountController$_closure4_closure5.doCall(content_hub_admin.SellingCompaniesAccountController:70) at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController:66) at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController) at java.lang.Thread.run(Thread.java:680) 

First of all, where do the hidden values ​​come from, and this approach is suitable for capturing Many-Many relationship information in the SellingCompaniesAccount controller class. The best technique for this.

Creator create.gsp resolves this in the browser: enter image description here

Thanks in advance

+6
save grails groovy has-many
source share
2 answers

If anyone else has the same problem, then Daniel's above answer is absolutely correct, just adding a few changes compared to Grails 2.7.8

All flags will have the same value = "$ {item.id}" and name = "salescompanies", as shown below -

 <!-- ... snip ... --> <tr class="prop"> <td valign="top" class="name"><label for="sellingCompanies"><g:message code="sellingCompaniesAccount.sellingCompanies.label" default="Selling Companies" /></label></td> <td valign="top" class=""> <g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i"> ${++i}. ${item.name}&nbsp;&nbsp;<g:checkBox name="sellingcompanies" optionKey="id" value="${item.id}" /> <br> </g:each> <!-- end here by rsheyeah --> </td> </tr> <!-- ... snip ... --> 

The best part is that in the current version of grails you do not need to use "flatten () as a string", as Daniel mentioned, and these values ​​will be automatically processed by grails and stored in the connection table. All you need to make sure your checkbox has the correct name and value.

Hope this helps!

+2
source share

The problem is this piece of code:

  params.each { if (it.key.contains("_sellingcompanies")) //sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer) if (it.key.contains("sellingcompanies_")) sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer) } 

Your parameters from the form message:

 Saving: ["accountInfo.id":"1", "accountInfo":["id":"1"], "_sellingcompanies_5":"", "_isActive":"", "code":"test", "agency_name":"test", "sellingcompanies_4":"4", "sellingcompanies_5":"5", "create":"Create", "isActive":"on", "iata":"test", "agency_id":"test", "contactinfo.id":"1", "contactinfo":["id":"1"], "consultant_id":"test", "sellingcompanies_2":"2", "_sellingcompanies_1":"", "sellingcompanies_3":"3", "_sellingcompanies_2":"", "_sellingcompanies_3":"", "sellingcompanies_1":"1", "_sellingcompanies_4":"", "action":"save", "controller":"sellingCompaniesAccount"] 

The test in your loop first checks to see if the key contains the " _ company sales" parameter, and then does nothing; the second part of which checks whether the key contains the parameter "salescompanies_", and then tries to extract the suffix number from this parameter value. In the case of the parameter with the key value "_sellingcompanies_5", both the first test and the second test are evaluated as true, so in the second test you subtract "salescompanies_" from the parameter key value, and you are left with "_5", which you then try to evaluate Integer. Unfortunately, "_5" is not a valid Integer value and therefore your given error.

You can solve this very simply by following these steps:

 params.each { if (it.key.startsWith("sellingcompanies")) { sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer) } } 

Probably the best way to handle this is to change your gsp to give the same named parameter for each of the selling companies, and then scroll through the actual application values. Something like that:

  <!-- ... snip ... --> <tr class="prop"> <td valign="top" class="name"><label for="sellingCompanies"><g:message code="sellingCompaniesAccount.sellingCompanies.label" default="Selling Companies" /></label></td> <td valign="top" class=""> <g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i"> ${++i}. ${item.name}&nbsp;&nbsp;<g:checkBox name="sellingcompanies" optionKey="id" value="${item.id}" /> <br> </g:each> <!-- end here by rsheyeah --> </td> </tr> <!-- ... snip ... --> 

Then in your controller do something like this:

 params.sellingcompanies = [params.sellingcompanies].flatten() as String[] sellingCompaniesAccountInstance.sellingCompaniesId = params.sellingcompanies.collect { SellingCompanies.get(it) } 

This should ensure that you correctly evaluate the corresponding values ​​that were passed from the model object, and not hack the search method.

Hope this helps!

+1
source share

All Articles