2

I have data writing to a mongoDB database with issues using integration tests and the Grails scaffolding. When trying to select a domain instance from the 'list' type page, I get the error "[domain name] not found with id null".

I am sure it is because of the Grails url [controller]/[action]/[id]. This id is a string and needs to be converted to an ObjectId for Grails queries.

Is there a way to do this so that it affects a specified domain or even better yet, all of the domains at once?

I guess as I'm writing my app, I can convert it to an ObjectId from within the action method, but I'd like to have the scaffolding work or provide a global solution.

tim_yates
  • 167,322
  • 27
  • 342
  • 338
Mike A
  • 71
  • 7
  • Any luck with this? I am facing the exact same issue. I have generated the scaffolding code, by grails install-template, I tried changing the controller and the views, no luck... – Iraklis Alexopoulos May 31 '13 at 18:40

4 Answers4

1

I believe this is happening because the show() method (that the Grails scaffolding functionality generates as an action) accepts an id parameter of type Long ie.

def show(Long id) {
    def suiteInstance = Suite.get(id)
    if (!suiteInstance) {
        flash.message = message(code: 'default.not.found.message', args: [message(code: 'suite.label', default: 'MyDomainClass'), id])
        redirect(action: "list")
        return
    }

    [suiteInstance: suiteInstance]
}

which binds the id parameter to the argument. Because the ObjectId can't be converted to a Long, it ends up being null, hence the call to MyDomainClass.get(id) fails with the error message.

You can get around this by overriding the show() action in your scaffolded controller so that it expects an ObjectId or String, but I would say the proper fix for this is to update the Grails scaffolding plugin so it is a little more liberal in the types of IDs it accepts.

AndrewW
  • 678
  • 6
  • 18
1

I had this problem as well. You can keep the domain object id as an ObjectId and update the controller as follows:

domain Object:

import org.bson.types.ObjectId;

class DomainObject {
        ObjectId id
        // Add other member variables...
}

Controller:

def show(String id) {
    def domainObjectInstance = domainObject.get(new ObjectId(id))
    if (!domainObjectInstance) {
        flash.message = message(code: 'default.not.found.message', args: [message(code: 'domainObject.label', default: 'DomainObject'), id])
        redirect(action: "list")
        return
    }

    [domainObjectInstance: domainObjectInstance]
}

You would also need to update your other controller methods that use id as well such as edit, update etc.

Additionally, if you want the grails default controller generation to work like this for all your domain objects you can update the template as coderLMN suggests.

Stuart
  • 3,226
  • 5
  • 24
  • 28
0

The get(params.id) call in show() method will NOT convert params.id String to an ObjectId object, so the domain instance will be null, then the following code takes you to list action with an error message:

if (!exampleInstance) {
    flash.message = message(code: 'default.not.found.message', args: [message(code: 'example.label', default: 'Example'), params.id])
    redirect(action: "list")
    return
}

Possible solutions:

  1. you can run "grails install-template" command, so that the scaffolding templates in src/templates/scaffolding/ directory can be modified. Then you have new scaffold ready to generate customized controllers, views, tests for all your Domain classes.

  2. A simpler solution is to define the id property as String instead of ObjectId. A String id will be equal to objectId.toString(), in this case your scaffold will work.

coderLMN
  • 3,076
  • 1
  • 21
  • 26
  • I am using the mongoDB plugin. I am able to create the data using the scaffold, but when I click the link to 'show' that particular domain instance, I get the error that I specified in my first post. – Mike A Dec 17 '12 at 05:30
  • In scaffolding framework, you should be seeing the 'show' page after successfully create an instance. Can you post the stacktrace error message here? What is the full url of the 'show' link? – coderLMN Dec 17 '12 at 05:50
  • That makes sense. I wouldn't know because even after I create an instance, I get the 'list' view with the same 'Client not found with id null' message. There is nothing in my console as far as a stacktrace. Full URL for the show link is: http://localhost:8080/app1/client/show/50cf3b8ab1a7f41c35d03253 I added some integration tests that save some client records and then retrieve them using strings and that works successfully. I don't understand why I am getting this error. – Mike A Dec 17 '12 at 15:37
  • Thanks for your help. I just changed the id property to a String. It is saving it as a string in MongoDB. The ObjectId reference is gone. To my surprise, it is still doing the same thing. Very puzzling – Mike A Dec 17 '12 at 17:10
  • You need to delete all the existing documents first, because their id will still be ObjectId. Use mongo console to find some document to ensure the _id is a instance of String. And you should restart you app after domain classes being modified. – coderLMN Dec 17 '12 at 17:20
  • I have been dropping the Mongo database before each run. That is how I could see that the IDs were now strings in the db vs. ObjectID. The only way around this is to generate the controllers and change the Long id parameter to String id in the actions. – Mike A Dec 17 '12 at 18:04
  • One would think that the controllers would just take Objects rather than a specific type. Or, since this is groovy, why even declare the type? – Mike A Dec 17 '12 at 18:12
  • There is no specification about the type of params.id in controller. In my case, an objectId string as id works fine with get() method. If you post you domain class, controller and list.gsp view here, the error would be easily located. – coderLMN Dec 17 '12 at 18:15
  • If your controller has some Long id definition, you should check your scaffold templates and re-generate your controller and views. – coderLMN Dec 17 '12 at 18:23
  • Here is my domain class: `class Client { String id String companyName String address1 String address2 String city String state String zipCode String phone String status Date dateCreated Date lastUpdated static constraints = { companyName maxSize:60, blank: false address1 maxSize:60, nullable: false address2 maxSize:60, nullable: true city maxSize:30, nullable:false state maxSize:2, nullable:false zipCode maxSize:10, nullable:false phone maxSize:20, nullable:false status nullable:true } static mapping = { companyName index:true } } ` – Mike A Dec 17 '12 at 23:03
  • Sorry. Code won't format. I am using that very simple domain and my controller has nothing in it except for def scaffold = true – Mike A Dec 17 '12 at 23:06
  • Just run `grails generate-all yourPackage.Client` to generate the controller and gsps first. Then you can trace the code to find where it goes wrong. – coderLMN Dec 18 '12 at 02:20
  • That's what I did. There are methods that are expecting Long id. I changed them to accept a Sting and then it works. – Mike A Dec 18 '12 at 06:21
  • Which version of grails are you using? – coderLMN Dec 18 '12 at 06:22
  • I am using Grails version 2.1.1 – Mike A Dec 18 '12 at 15:14
  • Can you post those methods which expect Long id? – coderLMN Dec 18 '12 at 15:17
0

In domain classes keep you id type as ObjectId and keep scaffold = true for all respective controllers.

In Domain class :

ObjectId id

In respective controller :

static scaffold = true

Clear all existing collections from Mongo

I guess that's sufficient to have Grails-Mongo app up & running, considering you have correctly configured mongo-plugin

swapy
  • 290
  • 2
  • 9