has_many through update issue
-
23-08-2019 - |
Question
Been trying to sort this for over a day now, and I am sure that it is something simple that I am missing.
I have a project, which can have one main category and two optional categories. My relevant code for the project model:
has_many :project_categories
has_one :optional_category_1,
:through => :project_categories,
:conditions => 'is_main_category = 0',
:order => 'category_id',
:source => :category,
:class_name => 'Category'
has_one :optional_category_2,
:through => :project_categories,
:conditions => 'is_main_category = 0',
:order => 'category_id DESC',
:source => :category,
:class_name => 'Category'
has_one :main_category,
:through => :project_categories,
:conditions => 'is_main_category = 1',
:source => :category,
:class_name => 'Category'
The relevant code from the Category class:
has_many :project_categories
has_many :projects, :through => :project_categories, :source => :project
and from the ProjectCategory class:
class ProjectCategory < ActiveRecord::Base
belongs_to :project
belongs_to :category
end
In my view:
Main Category: <%= f.select(:main_category, Category.find(:all, :order => 'parent_id, categories.desc').collect {|c| [c.display_name, c.id] }, :prompt => "Select a Main Category") %><br>
Optional Category 1: <%= f.select(:optional_category_1, Category.find(:all, :order => 'parent_id, categories.desc').collect {|c| [c.display_name, c.id] }, :prompt => "Select an Optional Category") %><br>
Optional Category 2: <%= f.select(:optional_category_2, Category.find(:all, :order => 'parent_id, categories.desc').collect {|c| [c.display_name, c.id] }, :prompt => "Select an Optional Category") %><br>
and in my controller:
@project.attributes = params[:project]
Ok, so when updating an existing project, I get the following error:
undefined method `update_attributes' for #<Class:0x82efce0>
and the relevant stack trace:
C:/Software/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/associations.rb:1255:in `main_category='
C:/Software/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2745:in `send'
C:/Software/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2745:in `attributes='
C:/Software/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2741:in `each'
C:/Software/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/base.rb:2741:in `attributes='
C:/Development/craftbits_rails/app/controllers/projects_controller.rb:85:in `manage_project'
Is it saying that there is an issue with main_category and that it is a generic class? But why? The association defines it correctly AFAIK.
Any help appreciated!
Vikram
Solution
I know this doesn't address the error you're getting, but I'd suggest using three one-to-many relationships instead of one many-to-many relationship.
The conventional purpose of has_many :through => ...
(many-to-many) is for when you have something like students
and classes
. A student can be in any number of classes. A class can have any number of students. Totally arbitrary numbers on both sides of the relationship.
But that isn't your situation here. Your projects can be in exactly one main category, one optional category 1, and one optional category 2. It's a totally different problem and it isn't the problem that has_many :through
is designed to solve.
I suggest this arrangement:
class Project < ActiveRecord::Base
belongs_to :main_category, :class_name => "Category",
:foreign_key => 'main_category_id'
belongs_to :optional_category_1, :class_name => "Category",
:foreign_key => 'optional_category_1_id'
belongs_to :optional_category_2, :class_name => "Category",
:foreign_key => 'optional_category_2_id'
end
class Category < ActiveRecord::Base
has_many :main_category_projects, :class_name => "Project",
:foreign_key => 'main_category_id'
has_many :optional_category_1_projects, :class_name => "Project",
:foreign_key => 'optional_category_1_id'
has_many :optional_category_2_projects, :class_name => "Project",
:foreign_key => 'optional_category_2_id'
end
Then you'll be able to do stuff like:
my_project.main_category
my_category.optional_category_1_projects
# etc...
OTHER TIPS
Maybe change the controller to something like:
@project = Project.find(params[:id])
@project.update_attributes(params[:project])
You may need to use main_category_id
in your view, i.e.
Main Category: <%= f.select(:main_category_id, ...) %>
You're calling update_attributes
, but main_category
isn't properly an attribute - it's an association. main_category_id
is an attribute.
Where are you getting @project
from? Are you just doing a normal Project.find(params[:project_id])
or something?
Tried throwing in a debugger
statement and seeing what class the @project is and what methods it has on it?