Rails 4 has many through - ruby-on-rails-4

Hi there I'm a rails newbie and am having a hard time understanding an error I am getting.
I have many to many relationship that consists of
Project -> one to many project_numbers (billing numbers)
project_number -> has id, project_id(fk), task_name and hours associated with a task.
Associate-> Employee/Developer performing work (id, name, ...)
Resource -> Join table containing ID, project_number_id (fk) and Associate_id (fk), hrs assigned, hours used
My Model is as follows
#project.rb
class Project < ActiveRecord::Base
database_connection = Rails.env
establish_connection database_connection.to_sym
self.table_name = 'projects'
belongs_to :client, :inverse_of => :project
has_many :project_numbers
end
#associate.rb
class Associate < ActiveRecord::Base
database_connection = Rails.env
establish_connection database_connection.to_sym
self.table_name = 'associates'
has_many :resources
#has_many :project_numbers, through: => :resources
end
#project_number.rb
class ProjectNumber < ActiveRecord::Base
database_connection = Rails.env
establish_connection database_connection.to_sym
self.table_name = 'project_numbers'
belongs_to :project, :inverse_of => :project_number
has_many :resources
#has_many :associates, through: => :resources
end
resource.rb
class Resource < ActiveRecord::Base
database_connection = Rails.env
establish_connection database_connection.to_sym
self.table_name = 'resources'
belongs_to :project_number
belongs_to :associate
end
Each time I uncomment the "has_many through" lines I receive an error viewing the project details page (show):
My show.html.erb has a the line highlighted
<dt>Project Hours: </dt> <dd><%= #project.project_numbers.sum(:hours_sold)%></dd>
With a rails error of:
/home/ruby/pda/app/models/project_number.rb:7: syntax error, unexpected =>
has_many :associates, through: => :resources
I have been through many blogs and think I have the relationship set up correctly however I don't understand the error at all. Why would a call to sum(hours) in project_number cause be triggering this error. Note: even with this line commented the next query against project_numbers fails. Can someone please help me understand what I am missing?

This line is syntactically wrong.
has_many :project_numbers, through: => :resources #wrong
This is the right syntax
has_many :project_numbers, :through => :resources # right
For Rails4,you can write it to
has_many :project_numbers, through: :resources
Do the same changes to the other association too.

Related

Can a model have two associations to the same model, using :through?

Let's say I have three models: Organization, Skills and Assessments.
Can an Assessment belong to two different Organizations, via different relations?
For example, an assessment may have happened at organization A, but was based on a skill belonging to organization B.
Below are my models and associations:
class Organization < ActiveRecord::Base
has_many :checklists
has_many :levels, :through => :checklists
has_many :sections, :through => :levels
has_many :skills, :through => :sections
has_many :assessments_using_own_checklists, :through => :skills, :source => :assessments
end
class Skill < ActiveRecord::Base
belongs_to :section
has_one :level, through: :section
has_one :checklist, through: :level
has_one :organization, through: :checklist
has_many :assessments
end
class Assessment < ActiveRecord::Base
belongs_to :skill
has_one :section, through: :skill
has_one :level, through: :section
has_one :checklist, through: :level
has_one :checklist_owner, through: :checklist, source: :organization
belongs_to :organization
end
Using the above, I can get an assessment's organization:
Assessment.last.organization # yields organization 1
I can also get an assessment's checklist_owner:
Assessment.last.checklist_owner # yields organization 2
But when I try to use checklist_owner in a where, the association seems to forget to use the :through. For example, if I run:
Assessment.where(organization: Organization.find(2), checklist_owner: Organization.find(1))
... this translates to SQL:
SELECT "assessments".* FROM "assessments" WHERE "assessments"."organization_id" = 2 AND "assessments"."organization_id" = 1
See how the SQL has two "assessments"."organization_id" = statements? Why does it do that?
Have you tried using joins?
something like:
Assessment.joins(skill: { section: { level: :checklist } }).where(organization: Organization.find(2), checklists: { organization_id: Organization.find(1) })
I know it look bad, but it seems that your relation from assessment to checklist is very complicated. This would take care of any weird relations being made.

Re-named associations working correctly

I have two types of users, one that can create movies and one that can create reviews:
class User < ActiveRecord::Base
has_many :created_movies, foreign_key: 'creator_id', class_name: 'Movie'
has_many :reviewed_movies, foreign_key: 'reviewer_id', :through => 'Review'
end
class Review < ActiveRecord::Base
belongs_to :movie
belongs_to :reviewer, class_name: 'User'
end
class Movie < ActiveRecord::Base
has_many :reviews
belongs_to :creator, class_name: 'User'
end
Whenever I try to run the following in my users/show:
<% if #user.reviewed_movies.any? %>
I get this problem:
ActiveRecord::HasManyThroughAssociationNotFoundError at /users/1
Could not find the association "Review" in model User
I can see that I can successfully populate the reviewer_id with the correct user when the review is created when I go into the command line:
m = Movie.last
m.reviews[1]
# => <Review:0x007fdc4036a938> {
# :id => 2,
# :rating => 2,
# :title => "bye",
# :content => "byeeeee",
# :created_at => Tue, 15 Sep 2015 19:20:25 UTC +00:00,
# :updated_at => Tue, 15 Sep 2015 19:20:25 UTC +00:00,
# :movie_id => 3,
# :reviewer_id => 1
# }
But I can't retrieve it from the other end. If anyone can provide some assistance it would be greatly appreciated, thanks!
ActiveRecord::HasManyThroughAssociationNotFoundError at /users/1 Could
not find the association "Review" in model User
The problem is here in this line
has_many :reviewed_movies, foreign_key: 'reviewer_id', :through => 'Review'
Which should be
has_many :reviewed_movies, foreign_key: 'reviewer_id', class_name: 'Review'
You need to tell your User model it has many reviews through which it has many movies called "reviewed_movies".
class User < ActiveRecord::Base
has_many :created_movies,
foreign_key: :movie_id,
class_name: 'Movie'
has_many :reviews
has_many :reviewed_movies,
foreign_key: :movie_id,
through: :reviews,
class_name: 'Movie'
end
EDIT: The foreign key is there to tell your model which foreign key to use, if your relation isn't called the same as the foreign-key, so I think it should be 'movie_id'... Ok, not sure about this any longer, I guess it depends on your setup ^^

ActiveAdmin has_many through relationship not updating param Rails

I'm working through creating a has_many: through relationship in active admin. Here are the models as they stand:
class Category < ActiveRecord::Base
has_many :subcategories
end
class Subcategory < ActiveRecord::Base
has_many :product_in_subcategories
has_many :products, through: :product_in_subcategories
accepts_nested_attributes_for :product_in_subcategories, :allow_destroy => true
belongs_to :category
end
class Product < ActiveRecord::Base
has_many :product_in_subcategories
has_many :subcategories, through: :product_in_subcategories
accepts_nested_attributes_for :product_in_subcategories, :allow_destroy => true
end
class ProductInSubcategory < ActiveRecord::Base
belongs_to :product
belongs_to :subcategory
end
In ActiveAdmin I have the permit_params and form like so:
ActiveAdmin.register Product do
# note some params that are product only have been removed for simplicity
permit_params :name, subcategory_id:[:id], product_in_subcategories_attributes: [:id, :subcategory_id, :product_id, :_create, :_update]
form do |f|
f.inputs
f.has_many :product_in_subcategories do |s|
s.input :subcategory_id, :as => :check_boxes, :collection => Subcategory.all
end
f.actions
end
end
The form populates as should, and will save everything except for the subcategory_id. If I enter into the DB a proper subcategory_id the box will show checked on edit.
The messages when saving give:
Unpermitted parameters: subcategory_id
However, it appears it is trying to submit this with the product, for which there isn't a subcategory_id. Any ideas on what I am doing incorrectly here? This is driving me nuts and I've read everything I can find. I'd really like to understand what I'm doing wrong. Thanks.
After much time spent on this one, I couldn't find a suitable solution except for this one, which is actually very nice. It in fact is not much different from my envisioned solution:
The only changes to the above code were made in ActiveAdmin:
ActiveAdmin.register Product do
# note some params that are product only have been removed for simplicity
permit_params :name, product_in_subcategories_attributes: [:id, :subcategory_id, :product_id, :_create, :_update]
form do |f|
f.inputs
f.has_many :product_in_subcategories do |s|
s.input :subcategory_id, :as => :select, :collection => Subcategory.all
end
f.actions
end
end
Very strange how this allows a select box with no issues, but it flips out over check boxes. Nonetheless, I'm happy with the solution.

Rails has_many through association not updating join model

I have the following models:
class Project < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
class User < ActiveRecord::Base
has_many :memberships
has_many :projects, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
and the migration for memberships
def change
create_table :memberships do |t|
t.belongs_to :user
t.belongs_to :project
t.boolean :admin
end
end
In my controller, I want to create a project for the current user
def create
#project = current_user.projects.build(project_params)
if #project.save
render json: #project, status: :ok
else
render json: { errors: #project.errors }, status: :unprocessable_entity
end
end
def project_params
params.require(:project).permit(:name)
end
When #project.save is called, the SQL in the log:
INSERT INTO "projects" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-12-09 17:18:55.298566"], ["name", "Test"], ["updated_at", "2014-12-09 17:18:55.298566"]]
So memberships is never updated, so the project gets made but it is not associated with the current user. I think rails is supposed to do that automatically when using the relationship to build the object, but that doesn't seem to be the case here.
It will work if I do
#project = current_user.projects.build(project_params)
#project.users << current_user
But that feels incorrect. Where am I going wrong?

Add items to a has_many relation on creation

I'm trying to make a has_many relation work for object to be created.
It is a simple case and despite many efforts and researches through the web, I cannot find why my code is not working.
I have the following classes (note: some variables use French names):
class Comptes::Category < ActiveRecord::Base
has_many :categorizations, dependent: :destroy
accepts_nested_attributes_for :categorizations
has_many :transactions, through: :categorizations
validates :nom, presence: true, uniqueness: true
end
class Comptes::Transaction < ActiveRecord::Base
has_many :categorizations, dependent: :destroy
accepts_nested_attributes_for :categorizations
has_many :categories, through: :categorizations
... # some validations
end
class Comptes::Categorization < ActiveRecord::Base
belongs_to :transaction
belongs_to :category
validates :transaction, presence: true
validates :category, presence: true
end
Category and Transaction are the basic models and Categorization is dedicated to the association (this is a basic account - transaction system).
What I can do is create a transaction and a category then fill transaction.categories with the category (transaction has thus an id).
What I cannot do is:
transaction = Comptes::Transaction.new ...
category = Comptes::Category.first
transaction.categories << category
# OR
transaction.categorizations.build category: category
# OR
# use categorizations_attributes in and accepts_nested_attributes_for.
Thank you very much for any help
Edit: this is done in rails 4.0.0
And I found that the issue was coming from the validation in Comptes::Categorization.
This prevents creation of new categorizations if the transaction or category does not exist yet.
Update (18/08/2014): the issue is coming from the validation in Categorizations, which prevent from creating the association without existing transaction and category. This may be an issue in rails 4.0.0. To see...
Transaction class is not under the module Comptes. Therefore, when you do has_many :categorizations or has_many :categories in it, the corresponding models are inferred as Categorization and Category instead of Comptes::Categorization and Comptes::Category.
To resolve this, you need to specify the class_name option of the association because the name of the model can't be inferred from the association name.
Update the class Transaction as below:
class Transaction < ActiveRecord::Base
has_many :categorizations, class_name: "Comptes::Categorization" , dependent: :destroy
accepts_nested_attributes_for :categorizations
has_many :categories, through: :categorizations, class_name: "Comptes::Category"
end