SitePoint Sponsor

User Tag List

Results 1 to 4 of 4
  1. #1
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    316
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question Mongoid relationships

    Hello, I am new to Rails and I'm building an API.

    I have 2 objects:
    Team: name, leader (user object, person who manages the team), users (array of user objects for people in the team (not including the leader))
    User: id, name, email, typical fields

    Currently my front end sends back a team object as name, users (array of user ids), leader (user id).

    My Create and Update look like this for team:
    Code Ruby:
      def update
        @team = Team.find(params[:id])
     
        if @team.update_attributes!(accepted_params)
          render json: @team
        else
          render json: @team.errors, status: :unprocessable_entity
        end
      end

    My Team model looks like this:
    Code Ruby:
    class Team
      include Mongoid::Document
      include Mongoid::Timestamps::Created
      include Mongoid::Timestamps::Updated
     
      # standard
      field :name
      # relationships
      has_one :leader, class_name: 'User'
      has_many :users, inverse_of: :team
     
      # define the users based on an array of people with the team id
      def users=(user_ids)
        self.users.clear
        user_ids.each do |id|
          User.find(id).update_attribute :team_id, self.id
        end
        self.users
      end
    end

    My User model looks like this:
    Code Ruby:
    class User
      include Mongoid::Document
      include Mongoid::Timestamps::Created
      include Mongoid::Timestamps::Updated
     
      field :name
      ## Database authenticatable
      field :email,              :type => String, :default => ""
     
      # users belong to a team
      belongs_to :team, inverse_of: :user
     
    end

    I have a team serialiser like this:
    Code Ruby:
    class TeamSerializer < ActiveModel::Serializer
      attributes :_id, :name
      has_many :users
      has_one :leader
    end

    The problem I have is that when I save the team (or create it), the leader object never gets saved, it seems to default leader to the first item in the users array.

    Can anyone help me get the relationships right? Many thanks for any assistance.

  2. #2
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,095
    Mentioned
    28 Post(s)
    Tagged
    1 Thread(s)
    Hi,

    Is it just the user_id of the Leader you are wanting to set when saving the Team?
    If so, then you just need to make sure that attribute is in your accepted_params and is getting passed through in the request.

    If you are wanting to update properties of the child record(User name, email) then you'll need to save that user explicitly, updating the attributes of the Team in your action won't update the children's attributes by default.

    The traditional way of doing this is updating both records.
    Code ruby:
    if @leader.update_attributes!(accepted_leader_params)
    end
    if @team.update_attributes!(accepted_team_params)
    end

    Rails does have accepts_nested_attributes_for for being able to save child attributes directly on the parent - but most people tend to stay away from that also.

    Haven't used mongoid in a while but another option may be to use their embedded object feature.
    Code ruby:
    class Team
      embeds_one :leader, class_name: 'User'
    end
    This has it's drawbacks though - you could never search for this leader outside of the team. It won't exist as a User document, only as part of the team. I don't think that's what you would want to do.

  3. #3
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    316
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by markbrown4 View Post
    Hi,

    Is it just the user_id of the Leader you are wanting to set when saving the Team?
    If so, then you just need to make sure that attribute is in your accepted_params and is getting passed through in the request.

    If you are wanting to update properties of the child record(User name, email) then you'll need to save that user explicitly, updating the attributes of the Team in your action won't update the children's attributes by default.

    The traditional way of doing this is updating both records.
    Code ruby:
    if @leader.update_attributes!(accepted_leader_params)
    end
    if @team.update_attributes!(accepted_team_params)
    end

    Rails does have accepts_nested_attributes_for for being able to save child attributes directly on the parent - but most people tend to stay away from that also.

    Haven't used mongoid in a while but another option may be to use their embedded object feature.
    Code ruby:
    class Team
      embeds_one :leader, class_name: 'User'
    end
    This has it's drawbacks though - you could never search for this leader outside of the team. It won't exist as a User document, only as part of the team. I don't think that's what you would want to do.

    My ideal team object would be:

    {
    • name: "Brands Team",
    • leader:
      {
      • name: "aaron",
      • email: "aaron@tb.com"

      },
    • users:
      [

      • {
        • name: "aaron",
        • email: "aaron@tb.com"

        },

      • {
        • name: "aaron",
        • email: "aaron@tb.com"

        }

      ]

    }

    I would prefer to use embedding because then I can query the team by its leader.

    I stripped everything back and now the only relationships stored are in my team model:
    Code Ruby:
    embeds_one :user, inverse_of: :leader, class_name: 'User'
      embeds_many :users, inverse_of: :users, class_name: 'User'

    However when I try and save the team I get this:
    Code Ruby:
    Started PUT "/teams/528dec4854eadc680c000001?authentication_token=zCutUnHa5pZUWVN34yLU" for 127.0.0.1 at 2013-11-21 11:21:57 +0000
    Processing by TeamsController#update as JSON
      Parameters: {"id"=>"528dec4854eadc680c000001", "name"=>"Brands", "holiday_days_per_year"=>20, "hours_per_day"=>8, "users"=>["528dec4454eadc4677000003"], "leader"=>"528dec4454eadc4677000003", "_id"=>"528dec4854eadc680c000001", "created_at"=>"2013-11-21T11:19:36.285Z", "description"=>nil, "organisation_id"=>1, "role"=>"leader", "slug"=>"brands", "updated_at"=>"2013-11-21T11:19:36.285Z", "authentication_token"=>"zCutUnHa5pZUWVN34yLU"}
      MOPED: 127.0.0.1:27017 QUERY        database=team_builder_dev collection=users selector={"$query"=>{"authentication_token"=>"zCutUnHa5pZUWVN34yLU"}, "$orderby"=>{:_id=>1}} flags=[:slave_ok] limit=-1 skip=0 batch_size=nil fields=nil (0.6428ms)
      MOPED: 127.0.0.1:27017 QUERY        database=team_builder_dev collection=users selector={"$query"=>{"authentication_token"=>"zCutUnHa5pZUWVN34yLU"}, "$orderby"=>{:_id=>1}} flags=[:slave_ok] limit=-1 skip=0 batch_size=nil fields=nil (0.5119ms)
    Can't verify CSRF token authenticity
      MOPED: 127.0.0.1:27017 QUERY        database=team_builder_dev collection=teams selector={"_id"=>"528dec4854eadc680c000001"} flags=[:slave_ok] limit=0 skip=0 batch_size=nil fields=nil (0.3591ms)
    Unpermitted parameters: id, _id, created_at, description, organisation_id, role, slug, updated_at, authentication_token
    Completed 500 Internal Server Error in 5ms
     
     
    NoMethodError (undefined method `metadata' for "528dec4454eadc4677000003":String):
      app/controllers/teams_controller.rb:41:in `update'
     
     
     
     
      Rendered /Users/azz0r/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.7ms)
      Rendered /Users/azz0r/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
      Rendered /Users/azz0r/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.9ms)
      Rendered /Users/azz0r/.rvm/gems/ruby-2.0.0-p247/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (11.9ms)

    Teams controller line 41:

    Code Ruby:
    def update
        @team = Team.find(params[:id])
     
     
        if @team.update_attributes!(accepted_params)
          render json: @team
        else
          render json: @team.errors, status: :unprocessable_entity
        end
      end

  4. #4
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,095
    Mentioned
    28 Post(s)
    Tagged
    1 Thread(s)
    Hi,

    I don't think you want to have everything embedded.

    Would you like to be able to do things like User.where(name: "aaron") ?
    If you go down this path you'll need to search through all teams just to find which one has the user you are looking for.

    I would prefer to use embedding because then I can query the team by its leader.
    No, you would just need to write something like @aarons_team = Team.where(leader_id: @aaron.id)

    The error you're getting is because the leader attribute being passed through in the request is just the id "528dec4454eadc4677000003", you're then trying to save the leader object with just that string.

    If you're still learning Rails I'd suggest steering clear of Mongoid altogether, for some things it makes sense but most times it just complicates a perfectly good simpler alternative. All of the guides are specific to relational db's so you're best learning Rails using something like Postgres.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •