I have a rails app where users have children. So I had two models: :users and :children.
I used to have a one-to-many relationship (has_many / belongs_to), but I wanted to open it up to a many-to-many relationship where I could store variables about the relationship. So I changed the relationship to a "has_many through" relationship. I created a table called :relationships. A small caveat: I want the foreign_key for user to be :parent_id. Here is how I have them set up:
Schema:
create_table "relationships", :force => true do |t|
t.integer "child_id"
t.integer "parent_id"
t.datetime "created_at"
.
.
.
create_table "users", :force => true do |t|
t.string "email", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "first_name"
t.string "last_name"
.
.
.
create_table "children", :force => true do |t|
t.string "name", :null => false
t.integer "parent_id", :null => false
t.datetime "created_at", :null => false
.
.
.
Class Definitions:
user.rb:
has_many :relationships, foreign_key: "parent_id"
has_many :children, :through => :relationships, foreign_key: "parent_id"
child.rb:
has_many :relationships, foreign_key: "child_id"
has_many :parents, :through => :relationships
relationship.rb:
belongs_to :parent, class_name: "User", :foreign_key => :parent_id
belongs_to :child, class_name: "Child", :foreign_key => :child_id
Now when I choose a specific user and try to get user.children, I get [] in response. If I try to add new children, it doesn't work either. I can define the parent and the child, but when it tries to save, It cannot associate the two. It doesn't see the parent, so I get the error:
*ActiveRecord::StatementInvalid (PG::Error: ERROR: null value in column "parent_id" violates not-null constraint*
If I switch the Class definitions back to a one-to-many setup, it accesses the children just fine. I don't understand what the problem is. Any help would be very appreciated.
Thanks
How are you building the relationship? #user.children works for me with the given example when I create the relationship as follows:
#user.relationships.build(child_id: ...)
in children table parent_id should be allowed to take null if you want create children as nested attributes.
Change
create_table "children", :force => true do |t|
t.string "name", :null => false
t.integer "parent_id", :null => false
t.datetime "created_at", :null => false
end
To
create_table "children", :force => true do |t|
t.string "name", :null => false
t.integer "parent_id"
t.datetime "created_at", :null => false
end
Related
class FixedLineItem < AR
belongs_to :fee_table, :class_name => 'FixedFeeTable',
:foreign_key => 'fixed_fee_table_id',
:inverse_of => :line_items,
:counter_cache => :fixed_line_items_count
end
class FixedFeeTable < AR
has_many :line_items, :class_name => 'FixedLineItem',
:inverse_of => :fee_table
end
Given the above, why does #fixed_fee_table.line_items.size still access the db? I can see it performing a COUNT operation in the console.
If i rename the has_many association to has_many :fixed_line_items, it works as expected.
Am i missing an option somewhere? I cant find any mention through googling however.
EDIT
This is on rails 3.2.14
and my schema for the two tables
create_table "fixed_fee_tables", :force => true do |t|
t.string "kind"
t.integer "total_amount_cents"
t.integer "fee_summary_id"
t.string "currency"
t.integer "final_total_amount_cents"
t.integer "fixed_line_items_count", :default => 0, :null => false
end
create_table "fixed_line_items", :force => true do |t|
t.string "description"
t.integer "quantity"
t.integer "price_cents"
t.integer "fixed_fee_table_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "amount_cents"
t.integer "position"
t.integer "total_adjustment_cents"
t.string "currency"
end
I followed http://railscasts.com/episodes/17-habtm-checkboxes-revised?view=asciicast tutorial to set up a has_many through relationship and when I try to access information from one model it works but not from the other.
I can access the Category information from the Product model via #product.category_ids and #product.categories, but the reverse isn't true. I can't access the Product information from the Category model. Using #category.product_ids or #category.products gives me the error NoMethodError: undefined method 'product_ids' for #<Category:0x007fa70d430e98>
Product.rb
class Product < ActiveRecord::Base
attr_accessible :category_ids
has_many :categorizations
has_many :categories, through: :categorizations
accepts_nested_attributes_for :categorizations, :allow_destroy => true
end
Category.rb
class Category < ActiveRecord::Base
attr_accessible :product_ids
has_many :categorizations
has_many :products, through: :categorizations
end
-- EDIT --
Schema.rb
ActiveRecord::Schema.define(:version => 20130926192205) do
create_table "categories", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "products", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "categorization", :force => true do |t|
t.integer "product_id"
t.integer "category_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "categorization", ["product_id", "category_id"], :name => "index_categorization_on_product_id_and_category_id", :unique => true
add_index "categorization", ["product_id"], :name => "index_categorization_on_product_id"
add_index "categorization", ["category_id"], :name => "index_categorization_on_category_id"
end
To access the records from each object you should be able to:
#category.products
and
#product.categories
That will give you the associated objects.
product_ids is not a attribute on a category and it does not have accepts_attributes_for :products like your category model so removing attr_accessible :product_ids should fix the error.
I followed this RailsCast to make a sortable list of items, which works great for one model, but I need to sort items that are organized by a join model, and can't figure out how to do it. Here is an example:
I want to organize all the cycles that are in a program by the cycle_order column.
The cycle_order column is in the cycles_programs table
For good measure, see picture of the join table at the bottom.
class Cycle
has_many :cycles_programs
has_many :programs, :through => :cycles_programs
accepts_nested_attributes_for :programs
accepts_nested_attributes_for :cycles_programs, allow_destroy: :true
class CyclesProgram
belongs_to :program
belongs_to :cycle
class Program
has_many :cycles_programs
has_many :cycles, :through => :cycles_programs
accepts_nested_attributes_for :cycles
accepts_nested_attributes_for :cycles_programs, allow_destroy: :true
Here is the Schema:
create_table "programs", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "cycles_programs", :force => true do |t|
t.integer "program_id"
t.integer "cycle_id"
t.integer "cycle_order"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "cycles", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Add a default scope to CyclesGroup
default_scope -> { order(:group_order) }
And the answer is so simple. Just add default_scope -> { order(:cycle)order) } to the CyclesProgram model
I'm getting the error unknown attribute: user_id durring execution of current_user.stories.build
class User < ActiveRecord::Base
has_many :stories, class_name: 'Story', foreign_key: 'user_id', dependent: :destroy
...
class Story < ActiveRecord::Base
belongs_to :user, class_name: 'User', foreign_key: 'user_id'
...
schema.rb
create_table "stories", :force => true do |t|
t.string "responsible"
t.string "descr"
t.string "state"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "email"
t.string "password_digest"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "name"
end
It doesn't contain 'user_id' field. Any ideas?
Kulbir is correct that you need to define a user_id column in your stories table, but doesn't explain the way to do that.
The correct way to make that change is to create a new migration. By convention, it should be called add_user_id_to_stories and would be created as follows (assuming you're using Rails 3+):
rails generate migration add_user_id_to_stories
If you run that, it should actually generate a migration that already contains the change you need to make, which should be something like:
add_column :stories, :user_id, :integer
As an aside when you're following the Rails conventions concerning association naming, which you are, you can actually skip a lot of the extra specification. In the User model, you can specify just has_many :stories and in the Story model specify belongs_to :user. Rails will assume the same class names and foreign keys you've specified.
You should have a user_id field in your stories table like below to define the association in your models.
create_table "stories", :force => true do |t|
t.integer "user_id"
t.string "responsible"
t.string "descr"
t.string "state"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
...
end
Edit
Check Emily's answer for detailed explanation.
you should use the new syntax and pass the fieldtype as symbol
add_column :stories, :user_id, :integer
I have an Entry model and a Category model, where an Entry can have many Categories (through EntryCategories):
class Entry < ActiveRecord::Base
belongs_to :journal
has_many :entry_categories
has_many :categories, :through => :entry_categories
end
class Category < ActiveRecord::Base
has_many :entry_categories, :dependent => :destroy
has_many :entries, :through => :entry_categories
end
class EntryCategory < ActiveRecord::Base
belongs_to :category
belongs_to :entry
end
When creating a new Entry, I create it by calling #journal.entries.build(entry_params), where entry_params is the parameters from the entry form. If any categories are selected, however, I get this error:
ActiveRecord::HasManyThroughCantDissociateNewRecords in Admin/entriesController#create
Cannot dissociate new records through 'Entry#entry_categories' on '#'. Both records must have an id in order to delete the has_many :through record associating them.
Note that the '#' on the second line is verbatim; it doesn't output an object.
I have tried naming my categories selectbox on the form to categories and category_ids but neither make a difference; if either is in the entry_params, the save will fail. If no categories are selected, or I remove categories from entry_params (#entry_attrs.delete(:category_ids)), the save works properly, but the categories don't save, obviously.
It seems to me that the problem is that an EntryCategory record is attempting to be made before the Entry record is saved? Shouldn't build be taking care of that?
Update:
Here's the relevant parts of schema.rb, as requested:
ActiveRecord::Schema.define(:version => 20090516204736) do
create_table "categories", :force => true do |t|
t.integer "journal_id", :null => false
t.string "name", :limit => 200, :null => false
t.integer "parent_id"
t.integer "lft"
t.integer "rgt"
end
add_index "categories", ["journal_id", "parent_id", "name"], :name => "index_categories_on_journal_id_and_parent_id_and_name", :unique => true
create_table "entries", :force => true do |t|
t.integer "journal_id", :null => false
t.string "title", :null => false
t.string "permaname", :limit => 60, :null => false
t.text "raw_body", :limit => 2147483647
t.datetime "created_at", :null => false
t.datetime "posted_at"
t.datetime "updated_at", :null => false
end
create_table "entry_categories", :force => true do |t|
t.integer "entry_id", :null => false
t.integer "category_id", :null => false
end
add_index "entry_categories", ["entry_id", "category_id"], :name => "index_entry_categories_on_entry_id_and_category_id", :unique => true
end
Also, saving an entry with categories works fine in the update action (by calling #entry.attributes = entry_params), so it does seem to me that the problem is only based on the Entry not existing at the point that the EntryCategory records are attempted to be created.
I tracked down the cause of this error to be within the nested_has_many_through plugin. It seems that the version I had installed was buggy; after updating to the most recent version, my build works again.
Why do you call
self.journal.build(entry_params)
instead of
Entry.new(entry_params)
If you need to create a new entry associated to a specific Journal, given a #journal, you can do
#yournal.entries.build(entry_params)