1

I have a couple models (Site and Server) that are related to eachother via has_many :through. they also both belong_to :user. in my sites/new view, I create a new site, and I create a new server using a nested form.fields_for :servers. Everything works as expected, except for that the server that ends up getting created doesn't have a user_id populated. How do i ensure it is?

My sites_controller new and create methods:

 def new
    @user = current_user
    @site = @user.sites.build
    @servers = @user.servers.all

    # let there be one server linked
    @site.site_servers.build

    # @user.servers.build if @user.servers.empty?
    @site.servers.build( :user_id => current_user.id ) if @site.servers.empty?

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @site }
    end
  end

  def create
    @site = current_user.sites.build(params[:site])

    respond_to do |format|
      if @site.save
        flash[:notice] = 'Site was successfully created.'
        format.html { redirect_to(@site) }
        format.xml  { render :xml => @site, :status => :created, :location => @site }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @site.errors, :status => :unprocessable_entity }
      end
    end
  end

If you notice the commented lines, those are things I tried that didn't work.

Models:

class User < ActiveRecord::Base
  acts_as_authentic

  has_many :sites
  has_many :servers
end

class Site < ActiveRecord::Base

  belongs_to :user

  has_many :site_servers
  has_many :servers, :through => :site_servers

  accepts_nested_attributes_for :site_servers, :allow_destroy => true
  accepts_nested_attributes_for :servers, :allow_destroy => true

  validates_presence_of :name, :on => :create, :message => "Name is required"
end

class Server < ActiveRecord::Base
  attr_encrypted :password, :key => '393b79433f616f445f652a752d', :attribute => 'crypted_password'

  belongs_to :user

  has_many :site_servers
  has_many :sites, :through => :site_servers

  validates_presence_of :url, :on => :create, :message => "URL is required."
  validates_presence_of :username, :on => :create, :message => "Username is required."
  validates_presence_of :password, :on => :create, :message => "Password is required."


  def name
    username + "@" + url
  end

  def to_s
    name
  end

end

class SiteServer < ActiveRecord::Base
  belongs_to :site
  belongs_to :server

  has_one :user, :through => :site
end

And here's my schema:

ActiveRecord::Schema.define(:version => 20091203045550) do

  create_table "servers", :force => true do |t|
    t.string   "url"
    t.string   "username"
    t.string   "crypted_password"
    t.integer  "port"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "site_servers", :force => true do |t|
    t.integer  "site_id"
    t.integer  "server_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "sites", :force => true do |t|
    t.string   "name"
    t.string   "url"
    t.string   "path"
    t.integer  "user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "username"
    t.string   "email"
    t.string   "crypted_password"
    t.string   "password_salt"
    t.string   "persistence_token"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end
0

2 Answers 2

2

Do you have a hidden field for the user_id in the server form?

<%= f.hidden_field :user_id %>

If not, the value is not getting passed back, even if you managed to properly set it. The line you have commented out would have worked, if you add a hidden field to the form.

@site.servers.build(:user_id => current_user.id) if @site.servers.empty?

I actually like the idea of setting the user id in the create method better, because otherwise you introduce the possibility of someone crafting up their own form submission and creating things under other people's user ids. I don't know if security is a big deal in your app, but I never trust a user id that is sent from a form.

Sign up to request clarification or add additional context in comments.

2 Comments

ahh. didn't realize I need to do that to persist the id. makes sense. i'll populate manually since you brought up the security issue. thanks.
Uhh yeah huge security issue there -- never do that.
1

I'm guessing the problem is in your create action. The new action just builds Ruby objects -- you need to make sure there is similar build code in the create action:

def create
  @site = current_user.sites.build(params[:site])
  @site.save
end

5 Comments

Thanks for the reply. I just pasted my create method above as well. I was already using the exact code you posted. I think the reason is because the server is being created as a child of site. Site just thinks it needs to maintain the relationship between site and server, which it does. How would site know that it also needs to tell server the right user_id too?
Does it need to be done manually? Seems kinda hackish, but maybe I could do: @site.servers.first.user_id = current_user.id in my create method.
I see what you're saying now, but am having a very hard time trying to figure out what you're trying to accomplish. Why are you using a has_many :servers, :through => :site_servers? I'm not understanding the join table's purpose. The way I see it, User should has_many :sites, Site should has_many :servers, and User should has_many :servers, through => :sites.
Because Servers should be able to be independently managed by a user. A user should be able to setup a bunch of Servers and wire them to Sites at will. Several sites could have the same servers in their collection.
In that case, yes you'd need to add that attribute manually. I'd add an accessor for the current_user attribute and set it in a callback method in the model.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.