Grails

Validation not triggered when using many-to-many mapping

Details

  • Type: Bug Bug
  • Status: Reopened Reopened
  • Priority: Critical Critical
  • Resolution: Unresolved
  • Affects Version/s: 1.3.2
  • Fix Version/s: 2.2
  • Component/s: Controllers, Persistence
  • Labels:
    None
  • Environment:
    MAC OSX 10.6.4 and Windows XP

Description

When trying to persist a User object that has a many-to-many relation with a Role object i am getting unexpected behaviour. This at least to my newbie understanding.

Using a form that submits the User params and trying to persist the user a strange thing occurs. The validation errors on the User object which are there are totally ignored and the User object is persisted.

Example:
I have created a User domain class which has a constraint on the firstname [it may not be blank] and email [must be an email adress].

static constraints = {
   firstname(blank:false)
   email(nullable:false, blank:false, email:true)
}

So I expect when the form params are submited to the controller the .save() or .validate() will complain when i submit a user with NO firstname and NO email.

But what is see in the database is that the user is persisted without any problems. Also the row in the ROLE_USERS table is nicely filled with the correct id's from the role and user.

My GSP create page submits the value's to the controller like this:

<g:textField name="firstname" value="${user?.firstname}" size="12"/>
..

and for the role data:

<g:select name="roles" from="${roles}" value="${user?.roles?.id}" optionKey="id" optionValue="name"/>

I checked the object details using NetBeans debugger and they inspectors tells me that indeed the User object is filled with a blank username, blank email and with a correct role inside it.

I also checked if a incorrect User is saved when there is no many-to-many mapping. So i have a single User object. Then the validation errors are correctly handled. In other words the User is then NOT persisted exactly I expect!

I am having this problem on Grails 1.3.1 and also 1.3.2

I am wondering what i am missing in this scenario?? Do need to persist the data in another way? or am i using the many-to-many in a wrong way?

class Role {

    String name
    String description
    
    static constraints = {
        name(blank:false, unique:true)
        description(nullable:true)
    }

    static hasMany = [users: User]
}
class User {

    String firstname
    String lastname
    String email
    String fogbugzToken

    static constraints = {
        firstname(blank:false)
        //email(email:true)
    }

    static hasMany = [roles: Role]
    static belongsTo = Role
}
class UserController { 
...
    def create = {
        def userInstance = new User()
        userInstance.properties = params
        return [userInstance: userInstance, roles: Role.list()]
    }

    def save = {
        def userInstance = new User(params)
        if (userInstance.save(flush: true)) {
            flash.message = "Created User $userInstance.id"
            redirect(action: "show", id: userInstance.id)
        }
        else {
            render(view: "create", model: [userInstance: userInstance])
        }
    }
...   
}

I generated a new Grails project 1.3.2 and attached it to this issue. Steps to reproduce are as follows:

  • start application with 'grails run-app [in the BootStrap.groovy there a already 2 roles that are pre-loaded]
  • go to create a user and press save without filling any of the textfield os all field empty

Result will be a saved User object with blank firstName and blank email..

If i can be of any more help please let me know,
see also: http://forum.springsource.org/showthread.php?p=308098#post308098

Kind Regards,
Marco Pas

  1. User Overzicht.png
    30 kB
    14/Jul/10 4:05 PM

Activity

Hide
Burt Beckwith added a comment -

I get these validation errors with your test app:

Property [email] of class [class grailstest.User] cannot be blank
Property [firstname] of class [class grailstest.User] cannot be blank

Show
Burt Beckwith added a comment - I get these validation errors with your test app: Property [email] of class [class grailstest.User] cannot be blank Property [firstname] of class [class grailstest.User] cannot be blank
Hide
Marco Pas added a comment -

That is correct that you see the validation errors, but despite that the User object is persisted. Check the User list view. I guess if there are validation errors the User object should not be persisted.

Show
Marco Pas added a comment - That is correct that you see the validation errors, but despite that the User object is persisted. Check the User list view. I guess if there are validation errors the User object should not be persisted.
Hide
Marco Pas added a comment -

The User list view after the validation errors are shown, but they have no effect on the persistence.

Show
Marco Pas added a comment - The User list view after the validation errors are shown, but they have no effect on the persistence.
Hide
Peter Ledbrook added a comment -

Confirmed. The validation is kicking in and failing, but the user instance is still being persisted. It's probably something to do with User belonging to Role and the new User instance always being added to a role (or vise versa). I guess a workaround is to check the results of the validation and discard() the object if validation failed.

Show
Peter Ledbrook added a comment - Confirmed. The validation is kicking in and failing, but the user instance is still being persisted. It's probably something to do with User belonging to Role and the new User instance always being added to a role (or vise versa). I guess a workaround is to check the results of the validation and discard() the object if validation failed.
Hide
Jens Lukowski added a comment -

I cannot reproduce this behaviour in 1.4.0 snapshot. Can anyone confirm?

Show
Jens Lukowski added a comment - I cannot reproduce this behaviour in 1.4.0 snapshot. Can anyone confirm?
Hide
Eddie Lu added a comment -

I met this bug too. I guess Grails do not validate linked domain class when perform a cascade update. Maybe this bug related to bug #5586("cascading validation of domain objects does not work"). http://jira.codehaus.org/browse/GRAILS-5586

Show
Eddie Lu added a comment - I met this bug too. I guess Grails do not validate linked domain class when perform a cascade update. Maybe this bug related to bug #5586("cascading validation of domain objects does not work"). http://jira.codehaus.org/browse/GRAILS-5586
Hide
Burt Beckwith added a comment -

Jens - this works the same in 1.4

Show
Burt Beckwith added a comment - Jens - this works the same in 1.4
Hide
Sabst added a comment -

It took me a while to understand this issue and maybe the following will help.
Discarding the instance does not work in my case but cleaning-up the wrongly created relationship is a workaround.
For example, add to UserController/save/else:

def li = []
userInstance.roles.each { li << it }
li.each { it.removeFromUsers(userInstance) }

(the processing is splitted in two steps to avoid concurrency errors).

Dealing with many-to-many relationships is currently a real pain and I hope there are plans to improve/test it more in the future.
(also see http://grails.1312388.n4.nabble.com/Many-to-many-relationships-and-beforeDelete-td3449051.html).

I'm still a Grails fan anyway

Show
Sabst added a comment - It took me a while to understand this issue and maybe the following will help. Discarding the instance does not work in my case but cleaning-up the wrongly created relationship is a workaround. For example, add to UserController/save/else: def li = [] userInstance.roles.each { li << it } li.each { it.removeFromUsers(userInstance) } (the processing is splitted in two steps to avoid concurrency errors). Dealing with many-to-many relationships is currently a real pain and I hope there are plans to improve/test it more in the future. (also see http://grails.1312388.n4.nabble.com/Many-to-many-relationships-and-beforeDelete-td3449051.html). I'm still a Grails fan anyway

People

Vote (6)
Watch (8)

Dates

  • Created:
    Updated:
    Last Reviewed: