I have an issue where my ActiveRecord::Relation isn't working I have 3 Db's Users,Games,Achievements The relation defined between them is such
Users
has_many :games
Games
belongs_to :user
has_many :achievements
Achievements
belongs_to :game
The problem is when i try to call
Game.where(:appid => game["appid"]).achievements.new
it gives me and error saying that
undefined method `achievements' for #<Game::ActiveRecord_Relation:0x730f9f8>
I am running on Ruby on Rails 4.1.8 and I have no clue why this is happening (I do have the belongs_to :game,index: true column in my Achievements table I can't think of why its not working)
You are getting an association here:
Game.where(:appid => game["appid"])
... this is realized as an array of objects (even if the sql returns no records, then it is still an array, although it is empty).
You need to select one of them ... probably the first, like this:
Game.where(:appid => game["appid"]).first.achievements.new
Or you can run through the values:
Game.where(:appid => game["appid"]).each { |game| game.achievements.new }
or some such.
Probably, you should select one model
Game.where(:appid => game["appid"]).first
and then get relational models
.achievements.new
The answer is in the exception. #where returns an ActiveRecord_Relation, not a Game, even if there is only one Game in the relation. So you really have an enumerable. i.e:
puts Game.where(:appid => game["appid"]).first.achievements.new
If you just want the first game that meets the criteria, you can use find_by
Game.find_by(:appid => game["appid"])
but I am not sure that's what you are looking for
Related
I am adding a feature to an old app that was not made by me, this along with being relatively new to RoR is leading to some confusion for me.
I have models called reponse, activity_point, and report
response has two parents, it belongs_to activity_point and report.
I am trying to access activity_points for a do block like so:
report.responses.activity_points.activity.each do |a|
Obviously that isn't working. I am getting the error message:
undefined method `activity_points' for []:ActiveRecord::Relation
Thanks to anyone who can help me with this little problem.
Or you can add something like this to your Report model
has_many :responses
has_many :activity_points, :through => :responses
has_many :activities, :through => :activity_points
then you can do this
report.activities.each do |a|
Another way to do this kind of thing, add a method to Report and joins from the other side (to get activity objects)
def activities
Activity.joins(:activity_points => :responses).where('responses.report_id = ?', id)
end
The point of doing all this, you don't want to create Ruby objects if you don't need to. Nested loops are also a potential problem with unique items and sorting.
Each response have several activity_points so you should iterate through responses. Also each activity_point has several activities, so:
report.responses.each do |r|
r.activity_points.each do |ap|
ap.activity.each do |a|
# Do your thing
end
end
end
First, when you write report.responses, this will return an ActiveRecord array. Since activity_points is an undefined method for arrays, you can't call it. So to call this method there is two conditions:
You have to tell your app which element of the array will call the method. For instance, report.responses.first.activity_points or report.responses.second.activity_points ...
Response model has to have a has_many: activity_points to call this method.
You could still also use a loop, but that will take multiple DB calls. Therefore, my solution involves direct database call for efficiency.
Activity.includes(activity_point: {responses: :report}).where(reports: {id: report.id}).each do |a|
#...
#...
end
I have the following models:
Car:
has_many :car_classes
CarClass:
belongs_to :Car
belongs_to :CarMainClass
CarMainClass:
has_many :car_classes
What I want to do is to count the amount of cars in CarClass grouped by the car_main_class_id but then linked to the main_class_symbol which is in CarMainClass.
The query I have now is:
CarClass.group(:car_main_class_id).count(:car_id) => {43=>79, 45=>4 ...}
Which is almost what I want, except that I end up only with the :car_main_class_id which I to be the :main_class_symbol from CarMainClass:
{"A1"=>79, "A2"=>4 ...}
I tried joining the tables and custom select options, but they didn't work.
Can this be done in a query in which I don't have to iterate through the main classes again?
Many thanks for your help!
Instead of having a SQL approach and using a "count/group by", you should look to a very simple feature of Rails ActiveRecords : the counter_cache column.
For example, you can add a column "car_classes_count" in the CarMainClass, and in CarClass class, you do like this :
CarClass:
belongs_to :car
belongs_to :car_main_class, :counter_cache => true
You can do the same with a column "car_class_count" in Car.
I don't know if it can help, but I had the same kind of problems when I started to develop with Rails. I tried to do some unsuccessful crazy SQL queries (queries that worked w/ sqlite, but did not w/ postgres) and I finally choose an other approach.
Try this:
CarClass.includes(:car_main_class => :car_classes)
.group(:car_main_class_id).map { |cc|
{ cc.car_main_class.main_class_symbol => cc.car_main_class.cars.size }
}
Although this is quite ugly - I agree with #Tom that you should try to think of more meaningful class names.
Working on a multi-tenant app where most of my models will have a tenant_id field so I can ensure read permissions by finding through the association (current_tenant.applications.find(params[:id])):
class Application < ActiveRecord::Base
belongs_to :tenant
has_many :app_questions, :conditions => proc {{:tenant_id => tenant_id}}, :dependent => :destroy
end
I like how this allows me to elegantly create a new AppQuestion with the tenant_id set automatically:
#application = current_tenant.applications.find(params[:app_question][:application_id])
#question = #application.app_questions.build(params[:app_question])
#...
Problem is, when I try to use includes() to eager-load the association it throws an error:
current_tenant.applications.where(:id => params[:id]).includes(:app_questions => :app_choices).first
NoMethodError (undefined method `tenant_id' for #<Class:0x007fbffd4a9420>):
app/models/application.rb:7:in `block in <class:Application>'
I could refactor so that I don't have to have the proc in the association conditions, but am wondering if anyone has a better solution.
The ref does say: "If you need to evaluate conditions dynamically at runtime, use a proc"
I've replied to the other question with more details trying to explain why this cannot work.
When they say dynamic is because the proc can be executed at runtime, but not in the context of an existing instance of the Application class because it doesn't exist when you invoke this relation
Application.where(:id => params[:id]).includes(:app_questions => :app_choices)
The ability for :conditions to accept a proc isn't documented in the ref. I suspect it doesn't work the way you guessed it might.
:conditions accepts either an SQL WHERE clause, or a hash that can be turned into on. It's inserted into the SQL that gets the :app_questions records, and if it's a proc it's only called once to get the snippet for the SQL statement to be constructed.
It might help to have a look at your database relationships. Should app_questions link to tenants or applications?
Assuming the relation itself works you could try preload instead of includes
Not sure this could fall in performance section as well as model/database section, so here goes....
Let's say I have 3 models:
Movie {
has_one :interest, :as => :resource
}
Song {
has_one :interest, :as => :resource
}
Story {
has_one :interest, :as => :resource
}
and ...
Interest {
belongs_to :resource, :polymorphic => true
}
Now, if I need a list of all interests for all movies, and I want to show also the date those Movies objects were created (to say how old they were), then I use the lookup on resource_type attribute and then #some_interest.resource.created_at.
The problem with this is if I have 100 movie interests, then I will get 101 queries right ? So linear degradation. I tried using :include => [:resource] in my query call, but it says cannot use include in polymorphic associations.
How can I either eager load or optimize this problem to avoid this severe degradation ??
Any help would be greatly appreciated !!
If you are using searchlogic, there is a special syntax to deal with polymorphic relationships that may help. You can search on the has side of the relationship by specifying the name of the related class suffixed with type.
E.g. given your models, you ought to be able to do something like this to get movies created in the last 90 days:
Interest.resource_movie_type_created_at_gt(Time.now-90.days)
Searchlogic sets up the join on the related model for you, which should allay performance concerns.
Of course you can always write your own SQL using the find_by_sql method.
PS. one very helpful trick is to turn on logging in the Rails console while writing searches. This allows you to see the SQL generated right in the console without having to dig through the logs.
I have a polymorphic association like this -
class Image < ActiveRecord::Base
has_one :approval, :as => :approvable
end
class Page < ActiveRecord::Base
has_one :approval, :as => :approvable
end
class Site < ActiveRecord::Base
has_one :approval, :as => :approvable
end
class Approval < ActiveRecord::Base
belongs_to :approvable, :polymorphic => true
end
I need to find approvals where approval.apporvable.deleted = false
I have tried something like this -
#approvals = Approval.find(:all,
:include => [:approvable],
:conditions => [":approvable.deleted = ?", false ])
This gives "Can not eagerly load the polymorphic association :approvable" error
How can the condition be given correctly so that I get a result set with approvals who's approvable item is not deleted ?
Thanks for any help in advance
This is not possible, since all "approvables" reside in different tables. Instead you will have to fetch all approvals, and then use the normal array methods.
#approvals = Approval.all.select { |approval| !approval.approvable.deleted? }
What your asking, in terms of SQL, is projecting data from different tables for different rows in the resultset. It is not possible to my knowledge.
So you'll have to be content with:
#approvals = Approval.all.reject{|a| a.approvable.deleted? }
# I assume you have a deleted? method in all the approvables
I would recommend either of the answers already presented here (they are the same thing) but I would also recommend putting that deleted flag into the Approval model if you really care to do it all in a single query.
With a polymorphic relationship rails can use eager fetching on the polys, but you can't join to them because yet again, the relationships are not known so the query is actually multiple queried intersected.
So in the end if you REALLY need to, drop into sql and intersect all the possible joins you can do to all the types of approvables in a single query, but you will have to do lots of joining manually. (manually meaning not using rails' built-in mechanisms...)
thanks for your answers
I was pretty sure that this couldn't be done. I wanted some more confirmation
besides that I was hoping for some other soln than looping thru the result set
to avoid performance related issues later
Although for the time being both reject/select are fine but in the long run I
will have to do those sql joins manually.
Thanks again for your help!!
M