tkit
tkit

Reputation: 8642

Multiple nested attributes of the same type

I have two models - "symbols" and "users". Among other attributes, symbols has "created_by_id" and "updated_by_id" attributes which are id's of users that created/updated a certain symbol entry.

Let's say I want to display the symbols table with their "symbol" attribute and the nested "created by" and "updated by" (user.username) attributes for each symbol entry. Resulting table should look something like this:

symbol     created_by    updated_by
------------------------------------
symbol1    username1     username2
symbol2    username1     username2

How can I achieve this? I guess I need accepts_nested_attributes_for :user and probably has_one :user (?) in my Symbol model. Do I also need belongs_to :user in my User model?

After the models are set up properly, how can I access the username of users associated with "created_by_id" and "updated_by_id" in my view?

I have an edit form where I used nested form like this (which works fine):

<%= form_for @symbol do |f| %>
  Symbol: 
  <%= f.text_field :symbol %>
  <%= f.fields_for :kanji do |kf| %>
    Meaning:
    <%= kf.text_field :meaning %>

    Onyomi:
    <%= kf.text_field :onyomi %>

    Kunyomi:
    <%= kf.text_field :kunyomi %>
  <% end %>
  <%= f.submit "Save" %>
<% end %>

but I couldn't figure out how to do something similar in this case where I have two nested attributes associated with the same symbol.

I'm new to rails so perhaps I got the whole idea of how to do this wrong. If there is a better than what I just explained how I want to do it, please correct me.

edit: Here are the models. The first one is actually called JSymbol but the problem remains the same.

class JSymbol < ActiveRecord::Base
  belongs_to :j_symbol_type
  has_one :kanji, :dependent => :destroy
  has_one :radical, :dependent => :destroy
  has_one :vocabulary, :dependent => :destroy

  attr_accessible :created_by_id, :updated_by_id, :symbol, :j_symbol_type_id, :level
  attr_accessible :kanji_attributes, :radical_attributes, :vocabulary_attributes

  accepts_nested_attributes_for :kanji, :radical, :vocabulary
end

And the Users model.

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable, :timeoutable

  attr_accessible :username, :email, :password, :password_confirmation, :remember_me

  validates :username, :presence => true, :uniqueness => true
end

This is of course without the user-jsymbol relation since I am not sure what kind of relation to add in there.. It should be something like has_one :user in JSymbol model and belongs_to :j_symbol in User model except it needs to be done twice somehow.. There is also the matter of calling .build method in the action method for each Symbol.

Upvotes: 0

Views: 147

Answers (2)

tkit
tkit

Reputation: 8642

After rmagnum2002 made me realize that I could use foreign_key parameter here, I started googling "rails multiple foreign key to same table", stumbled upon a bunch of outdated SO answers that didn't work and finally found this article which helped me write the code that works.

So, this is the relation that I put into my JSymbol model:

belongs_to :created_by, :class_name => User, :foreign_key => "created_by_id"
belongs_to :updated_by, :class_name => User, :foreign_key => "updated_by_id"

and this one is from Users model:

has_many :occurances_as_created_by, :class_name => JSymbol, :foreign_key => "created_by_id"
has_many :occurances_as_updated_by, :class_name => JSymbol, :foreign_key => "updated_by_id"

I didn't have to add anything in my action method, and this is what I put in the view:

<%= j_symbol.created_by.username %>
<%= j_symbol.updated_by.username %>

This is what happens in SQL if anyone is interested (which means it actually does it through separate select queries instead of joins so I might yet keep looking for a better solution):

JSymbol Load (0.3ms)  SELECT "j_symbols".* FROM "j_symbols" 
JSymbolType Load (0.3ms)  SELECT "j_symbol_types".* FROM "j_symbol_types" 
User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 7 LIMIT 1
User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 8 LIMIT 1
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 8 LIMIT 1
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 8 LIMIT 1
Rendered j_symbols/index.html.erb within layouts/j_symbols (7.8ms)
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 8 LIMIT 1

Upvotes: 0

rmagnum2002
rmagnum2002

Reputation: 11421

Something like this should work:

class Symbol < ActiveRecord::Base
  has_many :creators, class_name: "User",
                          foreign_key: "created_by_id"
  has_many :updaters, class_name: "User",
                          foreign_key: "updated_by_id" 

  belongs_to :creator, class_name: "User"
end

then use:

   symbol | symbol.creator.name |  symbol.updater.name

still, I am trying this on my app, so I am not sure 100% about it.

EDIT

class Symbol < ActiveRecord::Base
  has_one :creator, :class_name => User, :foreign_key => 'created_by_id'
  has_one :updater, :class_name => User, :foreign_key => 'updated_by_id'
end

Upvotes: 1

Related Questions