I am working on a Rails 4 application and recently got into a strange issue. I am looking for your help here. Kindly advise.
A small gist snippet has been created to understand the issue undefined method committed?
Just to summarize everything:
# app/models
class User < ActiveRecord::Base
has_many :responses, dependent: :destroy
end
class Response < ActiveRecord::Base
has_one :report
has_many :points
belongs_to :user
end
class Report < ActiveRecord::Base
belongs_to :response
end
class Point < ActiveRecord::Base
belongs_to :response
end
# config/routes.rb
resources :users do
resources :responses do
resources :action_plans
end
end
# app/controllers/action_plans_controller.rb
class ActionPlansController < ApplicationController
before_filter :response
def new
#report = #response.build_report
5.times do
#response.points.build
end
end
private
def response
#response = current_user.responses.find(params[:id])
end
end
Whenever, I am trying to hit:
http://localhost:3000/users/:user_id/responses/:id/action_plans/new
I get error that says: undefined method `committed?' for Response Object. What I am doing wrong here?
By defining a method called response in your controller you're overriding an internal getter used by Rails. To solve the problem, just use a different name for your before action. The common way of naming the action is to use set_<entity name> so set_response it is.
There is a Response class namespaced inside ActionDispatch (ActionDispatch::Response) and it is used throughout Rails. Can it be the case that you're actually hitting the response object instead of your model? Maybe use pry-rails to debug it from inside?
I have these three model:
class Project < ActiveRecord::Base
has_many :garden
end
class Garden < ActiveRecord::Base
belongs_to :project
has_one :garden_customer
validates_uniqueness_of :idjardin, :message => "Este codigo jardin ya esta utilizado"
end
class GardenCustomer < ActiveRecord::Base
belongs_to :garden
end
If I run project=Project.find(1) and then garden=project.garden.find(1) everything is ok. But when i try to get the garden customers I got a nil object instead of an empty array or values, that is:
garden.garden_customer
is nil.
I can't call any method on garden.garden_customer.
Example of code:
#project=Project.find(params[:project_id])
#garden = #project.garden.find(params[:garden_id])
#garden_customers = #garden.garden_customer.all
I got the error "undefined method `all' for nil:NilClass"
Since Gardenis a has_many, it should really be setup like has_many :gardens
Edit: Also garden_customer is setup as a has_onerelationships, so there is only 1 garden_customer per garden and so garden.garden_customer.all is wrong. garden.garden_customer itself would directly give you the associated garden_customer provided it was setup during creation.
I want to be able to use two columns on one table to define a relationship. So using a task app as an example.
Attempt 1:
class User < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end
So then Task.create(owner_id:1, assignee_id: 2)
This allows me to perform Task.first.owner which returns user one and Task.first.assignee which returns user two but User.first.task returns nothing. Which is because task doesn't belong to a user, they belong to owner and assignee. So,
Attempt 2:
class User < ActiveRecord::Base
has_many :tasks, foreign_key: [:owner_id, :assignee_id]
end
class Task < ActiveRecord::Base
belongs_to :user
end
That just fails altogether as two foreign keys don't seem to be supported.
So what I want is to be able to say User.tasks and get both the users owned and assigned tasks.
Basically somehow build a relationship that would equal a query of Task.where(owner_id || assignee_id == 1)
Is that possible?
Update
I'm not looking to use finder_sql, but this issue's unaccepted answer looks to be close to what I want: Rails - Multiple Index Key Association
So this method would look like this,
Attempt 3:
class Task < ActiveRecord::Base
def self.by_person(person)
where("assignee_id => :person_id OR owner_id => :person_id", :person_id => person.id
end
end
class Person < ActiveRecord::Base
def tasks
Task.by_person(self)
end
end
Though I can get it to work in Rails 4, I keep getting the following error:
ActiveRecord::PreparedStatementInvalid: missing value for :owner_id in :donor_id => :person_id OR assignee_id => :person_id
TL;DR
class User < ActiveRecord::Base
def tasks
Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
end
end
Remove has_many :tasks in User class.
Using has_many :tasks doesn't make sense at all as we do not have any column named user_id in table tasks.
What I did to solve the issue in my case is:
class User < ActiveRecord::Base
has_many :owned_tasks, class_name: "Task", foreign_key: "owner_id"
has_many :assigned_tasks, class_name: "Task", foreign_key: "assignee_id"
end
class Task < ActiveRecord::Base
belongs_to :owner, class_name: "User"
belongs_to :assignee, class_name: "User"
# Mentioning `foreign_keys` is not necessary in this class, since
# we've already mentioned `belongs_to :owner`, and Rails will anticipate
# foreign_keys automatically. Thanks to #jeffdill2 for mentioning this thing
# in the comment.
end
This way, you can call User.first.assigned_tasks as well as User.first.owned_tasks.
Now, you can define a method called tasks that returns the combination of assigned_tasks and owned_tasks.
That could be a good solution as far the readability goes, but from performance point of view, it wouldn't be that much good as now, in order to get the tasks, two queries will be issued instead of once, and then, the result of those two queries need to be joined as well.
So in order to get the tasks that belong to a user, we would define a custom tasks method in User class in the following way:
def tasks
Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
end
This way, it will fetch all the results in one single query, and we wouldn't have to merge or combine any results.
Extending upon #dre-hh's answer above, which I found no longer works as expected in Rails 5. It appears Rails 5 now includes a default where clause to the effect of WHERE tasks.user_id = ?, which fails as there is no user_id column in this scenario.
I've found it is still possible to get it working with a has_many association, you just need to unscope this additional where clause added by Rails.
class User < ApplicationRecord
has_many :tasks, ->(user) {
unscope(:where).where(owner: user).or(where(assignee: user)
}
end
Rails 5:
you need to unscope the default where clause
see #Dwight answer if you still want a has_many associaiton.
Though User.joins(:tasks) gives me
ArgumentError: The association scope 'tasks' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.
As it is no longer possible you can use #Arslan Ali solution as well.
Rails 4:
class User < ActiveRecord::Base
has_many :tasks, ->(user){ where("tasks.owner_id = :user_id OR tasks.assignee_id = :user_id", user_id: user.id) }
end
Update1:
Regarding #JonathanSimmons comment
Having to pass the user object into the scope on the User model seems like a backwards approach
You don't have to pass the user model to this scope.
The current user instance is passed automatically to this lambda.
Call it like this:
user = User.find(9001)
user.tasks
Update2:
if possible could you expand this answer to explain what's happening? I'd like to understand it better so I can implement something similar. thanks
Calling has_many :tasks on ActiveRecord class will store a lambda function in some class variable and is just a fancy way to generate a tasks method on its object, which will call this lambda. The generated method would look similar to following pseudocode:
class User
def tasks
#define join query
query = self.class.joins('tasks ON ...')
#execute tasks_lambda on the query instance and pass self to the lambda
query.instance_exec(self, self.class.tasks_lambda)
end
end
I worked out a solution for this. I'm open to any pointers on how I can make this better.
class User < ActiveRecord::Base
def tasks
Task.by_person(self.id)
end
end
class Task < ActiveRecord::Base
scope :completed, -> { where(completed: true) }
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
def self.by_person(user_id)
where("owner_id = :person_id OR assignee_id = :person_id", person_id: user_id)
end
end
This basically overrides the has_many association but still returns the ActiveRecord::Relation object I was looking for.
So now I can do something like this:
User.first.tasks.completed and the result is all completed task owned or assigned to the first user.
Since Rails 5 you can also do that which is the ActiveRecord safer way:
def tasks
Task.where(owner: self).or(Task.where(assignee: self))
end
My answer to Associations and (multiple) foreign keys in rails (3.2) : how to describe them in the model, and write up migrations is just for you!
As for your code,here are my modifications
class User < ActiveRecord::Base
has_many :tasks, ->(user) { unscope(where: :user_id).where("owner_id = ? OR assignee_id = ?", user.id, user.id) }, class_name: 'Task'
end
class Task < ActiveRecord::Base
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end
Warning:
If you are using RailsAdmin and need to create new record or edit existing record,please don't do what I've suggested.Because this hack will cause problem when you do something like this:
current_user.tasks.build(params)
The reason is that rails will try to use current_user.id to fill task.user_id,only to find that there is nothing like user_id.
So,consider my hack method as an way outside the box,but don't do that.
Better way is using polymorphic association:
task.rb
class Task < ActiveRecord::Base
belongs_to :taskable, polymorphic: true
end
assigned_task.rb
class AssignedTask < Task
end
owned_task.rb
class OwnedTask < Task
end
user.rb
class User < ActiveRecord::Base
has_many :assigned_tasks, as: :taskable, dependent: :destroy
has_many :owned_tasks, as: :taskable, dependent: :destroy
end
In result, we can use it so:
new_user = User.create(...)
AssignedTask.create(taskable: new_user, ...)
OwnedTask.create(taskable: new_user, ...)
pp user.assigned_tasks
pp user.owned_tasks
I have a users_manager engine which has a User model class.
In an other shopping engine, I add some associations in the User model with the code below, in shopping/lib/shopping.rb:
module Shopping
class Engine<Rails::Engine
initializer :shopping_append_user do
UsersManager::User.class_eval do
has_many :products,:class_name=>"Shopping::Product"
has_many :virtues,:class_name=>"Shopping::Virtue"
has_many :containers,:class_name=>"Shopping::Container"
has_many :concerns,:class_name=>"Shopping::Concern"
has_many :remarks,:class_name=>"Shopping::Remark"
has_many :praisings,:class_name=>"Shopping::Praising"
has_one :cart,:class_name=>"Shopping::Cart"
has_one :shop_information,:class_name=>"Shopping::ShopInformation"
has_many :comments,:class_name=>"Shopping::Comment"
has_many :created_orders,:class_name=>"Shopping::Order",:foreign_key=>"creator_id"
has_many :processing_orders,:class_name=>"Shopping::Order",:foreign_key=>"processor_id"
end
end
initializer :shopping_append_file do
Upload::File.class_eval do
has_many :image_uuids,:class_name=>"Shopping::ImageUuid"
end
end
end
def self.table_name_prefix
"shopping_"
end
end
After running rails server, the application works fine. However, after modifying one controller file, I browse the web page and it gives me the following message :
undefined method `products' for #<UsersManager::User:0x00000003022a58>
How does rails reload the file after modifying them? How can I make my engine work right?
My version of rails is 3.2.0.pre from github, Ruby is 1.9.0.
Your initializer isn't reloaded on every request, this means that your customizations on the UsersManager::User class are lost when it is reloaded.
You can do the following instead:
module Shopping
class Engine < Rails::Engine
config.to_prepare do
Shopping.customize_user
Shopping.customize_file
end
end
def self.customize_user
UsersManager::User.class_eval do
has_many :products,:class_name=>"Shopping::Product"
has_many :virtues,:class_name=>"Shopping::Virtue"
has_many :containers,:class_name=>"Shopping::Container"
has_many :concerns,:class_name=>"Shopping::Concern"
has_many :remarks,:class_name=>"Shopping::Remark"
has_many :praisings,:class_name=>"Shopping::Praising"
has_one :cart,:class_name=>"Shopping::Cart"
has_one :shop_information,:class_name=>"Shopping::ShopInformation"
has_many :comments,:class_name=>"Shopping::Comment"
has_many :created_orders,:class_name=>"Shopping::Order",:foreign_key=>"creator_id"
has_many :processing_orders,:class_name=>"Shopping::Order",:foreign_key=>"processor_id"
end
end
def self.customize_file
Upload::File.class_eval do
has_many :image_uuids,:class_name=>"Shopping::ImageUuid"
end
end
def self.table_name_prefix
"shopping_"
end
end
The config.to_prepare block is run once in production and before every request in development (source).
I've been bashing my head against a wall for a while on this one and I can't get it to work. I have three models:
class Instrument < ActiveRecord::Base
has_many :analytical_methods
has_many :analytes, :through => :analytical_methods
accepts_nested_attributes_for :analytical_methods
attr_accessible :name, :analytical_methods_attributes
end
class AnalyticalMethod < ActiveRecord::Base
belongs_to :instrument
has_many :analytes
accepts_nested_attributes_for :analytes
attr_accessible :name, :analytes_attributes
end
class Analyte < ActiveRecord::Base
belongs_to :analytical_method
attr_accessible :name
end
And I have the following factories:
Factory.define :analyte do |analyte|
analyte.name "Test analyte"
end
Factory.define :analytical_method do |analytical_method|
analytical_method.name "Test method"
analytical_method.association :analyte
end
Factory.define :instrument do |instrument|
instrument.name "Test instrument"
instrument.association :analytical_method
instrument.association :analyte
end
Any time I try to Factory(:instrument) or Factory(:analytical_method), it throws the following error:
NoMethodError:
undefined method `analyte=' for #<AnalyticalMethod:0x00000104c44758>
Am I missing some ridiculous typo or something? The website works perfectly fine, but the tests keep failing. Thanks for any help in returning my sanity!
I believe it's because you're using instrument.association :analyte and analytical_method.association :analyte for a has_many relationship. The association declaration is used for belongs_to relationships.
I typically don't use Factory Girl to create has_many relationships, but if you choose to go this route, you're not the first person to do so. Here's a blog post that's a few years old, but seems to describe what you're trying to do.