jesal
jesal

Reputation: 7938

Rails 4, Devise & Polymorphic Associations

Here's my functioning code to register a type of user called mentor via a Rails 4 JSON API.

Now I'm wondering, is there a better way to go about this? A more cleaner/simpler approach where Rails can automagically create the user/mentor association.

Currently I'm setting it manually in the create method which doesn't seem right. So I just want to make sure there is no better way out there of going about this.

models/user.rb

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

  belongs_to :role, :polymorphic => true
end

models/mentor.rb

class Mentor < ActiveRecord::Base
  has_one :user, as: :role
  accepts_nested_attributes_for :user
end

controllers/api/V1/mentors_controller.rb

class Api::V1::MentorsController < ApplicationController
  respond_to :json

  def create
    @user = User.new(user_params)
    @mentor = Mentor.new(mentor_params)
    @user.role = @mentor
    @user.save!
    @mentor.user_id = @user.id
    @mentor.save!
    respond_with :api, @mentor
  end

  private

  def mentor_params
    params.require(:mentor).permit(:first_name, :last_name)
  end

  def user_params
    params.require(:user).permit(:email, :password)
  end
end

UPDATE - 10/01/2013

I make some more inroads with this. Here's what I have now:

controllers/api/V1/mentors_controller.rb

class Api::V1::MentorsController < ApplicationController
  respond_to :json

  def create
    @mentor = Mentor.new(mentor_params)
    @mentor.user.save!
    @mentor.user_id = @mentor.user.id
    @mentor.save!
    respond_with :api, @mentor
  end

  private

  def mentor_params
    params.require(:mentor).permit(:first_name, :last_name, user_attributes: [:email, :password])
  end
end

But I still have to set the user_id manually. Only doing Mentor.create(mentor_params) fails to set the user_id. Any way to get around that?

Upvotes: 0

Views: 692

Answers (2)

edean
edean

Reputation: 11

To answer the question on your update, try adding the id to your params whitelist:

def mentor_params
    params.require(:mentor).permit(:first_name, :last_name,user_attributes: [:id, :email, :password])
end

Upvotes: 1

Alex.Bullard
Alex.Bullard

Reputation: 5563

This is off the top of my head but the basic idea goes something like this.

Create a form with a nested resource

form_for @mentor do |f|
  f.input :mentor_val
  f.fields_for :user do |m|
    m.input :user_val

It should post a params object with a format something like this:

mentor: {
  mentor_val: 'blah'
  user_attributes: {
    user_val: 'foo'
  }
}

Now since you have included accepts_nested_attributes_for in your Mentor model, Rails automatically added a user_attributes= method to Mentor which will build the user model, including setting the relationship. This means that to create both models, all you need to do in the controller is call

@mentor.create(params)

Upvotes: 1

Related Questions