Announcement Announcement Module
Collapse
No announcement yet.
Is possible to create a domain and its related domain objects in one line? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Is possible to create a domain and its related domain objects in one line?

    Hello,

    I'm part of a team of developers and we're working in our second grails project. We're still learning some great features of grails and one question has come to our mind.

    We have some domain classes with relationships to others. My question is.. is there a way to hydrate (fill in the attributes of the domain with, for example, the params map) a domain object and its relationships?
    For example, if I have these two domain classes:

    User{
    String name
    String NIF
    static belongsTo = [profile:Profile]
    }

    Profile {
    String profileName
    }

    and I have a form like this one:

    <g:form name="userForm" action="save">
    <g:textField name="name" value="${userInstance?.name}"/>
    <g:textField name="NIF" value="${userInstance?.NIF}"/>
    <g:textField name="profile.profileName" value="${userInstance?.profile?.profileName}"/>
    <g:submitButton name="create" value="Create"/>
    </g:form>

    could we in the controller just write this to save the user and the profile?

    def save = {
    def userInstance = new User(params)
    user.save()
    ...
    }

    Many thanks.

  • #2
    Yes, that should work. Is it not?

    Comment


    • #3
      Is possible to create a domain and its related domain objects in one line?

      I have tried with the following code and it doesn't work.

      Gives this error:
      org.hibernate.PropertyValueException: not-null property references a null or transient value: pruebadomain.User.profile
      at $Proxy11.saveOrUpdate(Unknown Source)
      at pruebadomain.UserController$_closure4.doCall(prueb adomain.UserController:27)
      at pruebadomain.UserController$_closure4.doCall(prueb adomain.UserController)
      at java.lang.Thread.run(Thread.java:619)


      def create = {
      def userInstance = new User()
      userInstance.properties = params
      def profileInstance = new Profile()
      userInstance.profile = profileInstance
      return [userInstance: userInstance]
      }

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


      and in the view

      <g:form action="save" >
      <div class="dialog">
      <table>
      <tbody>

      <tr class="prop">
      <td valign="top" class="name">
      <label for="NIF"><g:message code="user.NIF.label" default="NIF" /></label>
      </td>
      <td valign="top" class="value ${hasErrors(bean: userInstance, field: 'NIF', 'errors')}">
      <g:textField name="NIF" value="${userInstance?.NIF}" />
      </td>
      </tr>

      <tr class="prop">
      <td valign="top" class="name">
      <label for="name"><g:message code="user.name.label" default="Name" /></label>
      </td>
      <td valign="top" class="value ${hasErrors(bean: userInstance, field: 'name', 'errors')}">
      <g:textField name="name" value="${userInstance?.name}" />
      </td>
      </tr>

      <tr class="prop">
      <td valign="top" class="name">
      <label for="profile"><g:message code="user.profile.label" default="Profile" /></label>
      </td>
      <td valign="top" class="value ${hasErrors(bean: userInstance, field: 'profile', 'errors')}">
      <g:textField name="profile.profileName" value="${userInstance?.profile?.profileName}"/>
      </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>

      Comment


      • #4
        OK, I see what the problem is. In order to do this, you need the save to cascade from the user to the profile. You enable cascading saves via the belongsTo property, which you have but on the wrong class. If you're saving User, then Profile should have a belongsTo to User.

        With the current relationships, you need to bind data to Profile and save that.

        Hope that helps.

        Comment


        • #5
          You're right, thanks.

          I have changed my domain classes to the following:

          class Profile {
          String profileName

          static belongsTo = [user:User]
          static constraints = {
          }
          }

          class User {
          String name
          String NIF
          Profile profile

          static constraints = {
          }
          }

          This works, but I had to add the profile field in the user domain to be able to create the user and the profile as I said in my last post. Is this ok?

          Comment


          • #6
            Yes, that's fine. I actually recommend using the hasOne property for one-to-one relationships:
            Code:
            class User {
                ...
                static hasOne = [ profile: Profile ]
                ...
            }

            Comment


            • #7
              ok, I'll follow your recomendation for one-to-one relationships.

              Thanks for the help

              Comment

              Working...
              X