It has been three years since I messed with one application of mine. It has always been working, and today, I noticed the following error after run “bundle install”:
File 1 : product.rb
class Product < ApplicationRecord
belongs_to :company
belongs_to :user
belongs_to :sell_unit
belongs_to :currency
has_many :lots
...
File 2 : currency.rb
class Currency < ApplicationRecord
belongs_to :product
...
File 3 : lot.rb
class Lot < ApplicationRecord
belongs_to :product
belongs_to :user
belongs_to :currency
...
def register_new
...
x = self.new {:company_id=>2,
:product_id=>1,
:user_id=>2,
:price=>"10000.0",
:trigger_id=>2,
:label=>"dd1f7a132e59",
"quantity"=>"1",
"expires_at"=>"2022-10-25 19:44:13 +0100",
:currency_id=>1,
:state_id=>5,
:_active=>true,
:activation_pending=>true}
x.save
Calling x.save => It produce the following instructions in order to proceed:
Product Load (0.3ms) SELECT `products`.* FROM `products` WHERE `products`.`id` = NULL LIMIT 1
↳ app/models/lot.rb:54:in `register_new'
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = NULL LIMIT 1
↳ app/models/lot.rb:54:in `register_new'
Currency Load (0.2ms) SELECT `currencies`.* FROM `currencies` WHERE `currencies`.`id` = NULL LIMIT 1
↳ app/models/lot.rb:54:in `register_new'
x.erros...
{:product=>["must exist"], :user=>["must exist"], :currency=>["must exist"]}
Why do the intermediate queries assume ID = NULL, if I even pass in the values?
`products`.`id` = NULL LIMIT 1
`users`.`id` = NULL LIMIT 1
`currencies`.`id` = NULL
Related
I'm learning rails and trying out polymorphic association. I have listed below a couple of simple models for illustration. Model associations seems to works fine as expected. But what if a user (commenter) would like to leave a comment for a another user? I can't seems to get it to work with these configuration. How do I go about doing so?
class User < ApplicationRecord
# username, email, password_digest
has_many :comments, as: :commentable, dependent: :destroy
end
class Project < ApplicationRecord
# title, completed, user_id
has_many :comments, as: :commentable, dependent: :destroy
end
class Comment < ApplicationRecord
# commenter_id, commentable_type, commentable_id, body
belongs_to :commentable, polymorphic: true
end
in console... setup
user1 = User.frist
user2 = User.last
project = Project.first
pro_comment = project.comments.new(body: 'some text')
pro_comment.commenter_id = user1.id
pro_comment.save
user_comment = user2.comments.new(body: 'some text')
user_comment.commenter_id = user1.id
user_comment.save
expected and actual results
Comment.all => successfully lists pro_comment & user_comment
But...
Comment.find_by(commenter_id: 1) => only listed the pro_comment
(what am I doing wrong?)
Also..
user1.comments => returned an empty object... was expecting 2 objects,
as you can see below it's not referencing 'commenter_id' ....
result...
comment Load (0.5ms) SELECT "comments".* FROM "comments" WHERE
"comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2
LIMIT $3 [["commentable_id", 1], ["commentable_type", "User"],
["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
I also tried ...
user1.comments.where(commenter_id: 1) >> which returned...
comment Load (0.4ms) SELECT "comments".* FROM "comments" WHERE
"comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2
AND "comments"."commenter_id" = $3 LIMIT $4 [["commentable_id", 1],
["commentable_type", "User"], ["commenter_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation []>
Not sure what I'm doing wrong. Could someone please point me in the right direction.
I thank you for your time.
find_by returns only one record, try Comment.where(commenter_id: 1) instead.
For user1.comments being empty, you are mixing the relationships. You should have 2 relationships: comment belongs to a commentable object (a project or a user) and comments also belongs to a commenter (the user you set as commenter_id).
It makes sense for user1.comments to be empty since the user is the commenter on both comments, it's not the commentable. user2.comments shouldn't be empty, same for project.comments
Try something like this:
class User < ApplicationRecord
has_many :comments_done, class_name: 'Comment', inverse_of: :commenter
has_many :comments, as: :commentable, dependent: :destroy
end
class Comment < ApplicationRecord
belongs_to :commenter, class_name: 'User'
belongs_to :commentable, polymorphic: true
end
(check the guide, I may be missing some config option https://guides.rubyonrails.org/v5.2/association_basics.html#has-many-association-reference)
Now you can use user1.comments_done and user1.comments for comments done by the user and done at the user.
Let's say I have the following two models, joined separately by the following two joins models:
class Game
has_many :game_plays
has_many :game_views
end
class Person
has_many :game_plays
has_many :game_views
end
# Games that a Person has played
class GamePlay
belongs_to :game
belongs_to :person
end
# Games that a Person has viewed
class GameView
belongs_to :game
belongs_to :person
end
Given a specific GamePlay, I want to get the GameView for the same Person-Game combo, e.g:
game_play = GamePlay.first
game_view = GameView.find_by(game_id: game_play.game_id, person_id: game_play.person_id)
I also need to eager load that association.
I'd love to create an association between GamePlay and GameView, but nothing I've tried has worked so far.
Attempt 1
class GamePlay
belongs_to :game
belongs_to :person
has_one :game_view, -> (gp) { where(game_id: gp.game_id) }, foreign_key: :person_id, primary_key: :person_id
end
This works, but I can't include this:
GamePlay.includes(:game_view).first
# => ArgumentError: The association scope 'game_view' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.
Attempt 2
class GamePlay
belongs_to :game
belongs_to :person
def game_view
GameView.find_by(game_id: game_id, person_id: person_id)
end
end
This obviously works, but I can't include this because it isn't defined as an association.
Any thoughts? Thanks!
Rails 5.0.0postgres 9.6.2
How about:
class GamePlay < ApplicationRecord
belongs_to :game
belongs_to :person
has_one :game_view, through: :person, source: :game_views
end
irb(main):002:0> GamePlay.includes(:game_view).find(2)
GamePlay Load (0.2ms) SELECT "game_plays".* FROM "game_plays" WHERE "game_plays"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Person Load (0.2ms) SELECT "people".* FROM "people" WHERE "people"."id" = 1
GameView Load (0.2ms) SELECT "game_views".* FROM "game_views" WHERE "game_views"."person_id" = 1
=> #<GamePlay id: 2, game_id: 1, person_id: 1>
irb(main):008:0> GamePlay.find(2).game_view
GamePlay Load (0.1ms) SELECT "game_plays".* FROM "game_plays" WHERE "game_plays"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
GameView Load (0.2ms) SELECT "game_views".* FROM "game_views" INNER JOIN "people" ON "game_views"."person_id" = "people"."id" WHERE "people"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<GameView id: 2, game_id: 1, person_id: 1>
I had the same challenge, I solved this with a custom version of Rails preloader https://github.com/2rba/smart_preloader
The association is exactly as you described:
class GamePlay
belongs_to :game
belongs_to :person
has_one :game_view, -> (gp) { where(game_id: gp.game_id) }, foreign_key: :person_id, primary_key: :person_id
end
and then explicitly call preloader as:
game_plays =GamePlay.all
ActiveRecord::SmartPreloader.(game_plays, ActiveRecord::CompositeKey.new(:game_view, [:game_id, :person_id])
that is pretty much the same as Rails default GamePlay.preloads(:game_view) behaviour which under the hood calls ActiveRecord::Associations::Preloader. The only difference preloader called explicitly, and preloader is slightly modified to support multikey and polymorphic associations.
I'm following official documentation: http://guides.rubyonrails.org/association_basics.html at section 4.3.3.4
I have following models:
class Nomination < ActiveRecord::Base
belongs_to :festival
has_many :festival_histories, -> { includes :awards }
attr_accessible :name
end
class FestivalHistory < ActiveRecord::Base
has_many :awards
belongs_to :nomination
belongs_to :festival
end
class Award < ActiveRecord::Base
belongs_to :festival_history
belongs_to :case, inverse_of: :awards
has_attached_file :image
attr_accessible :name, :festival_history_id, :image
end
Which looks very similar (for me) to example in documentation.
But when I do in console:
n = Nomination.first
n.festival_histories.awards
I get
NoMethodError: undefined method `awards' for #<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_FestivalHistory:0x000001019cd400>
I've reloaded console, so issue is not there for sure...
There is no problem with documentation:)
As JTG said, you couldn't get awards on all festival_histories, only on specific history.
The difference is:
With include option:
n = Nomination.first
Nomination Load (0.4ms) SELECT "nominations".* FROM "nominations" ORDER BY "nominations"."id" ASC LIMIT 1
n.festival_histories
FestivalHistory Load (25.5ms) SELECT "festival_histories".* FROM "festival_histories" WHERE "festival_histories"."nomination_id" = ? [["nomination_id", 1]]
Award Load (0.7ms) SELECT "awards".* FROM "awards" WHERE "awards"."festival_history_id" IN (1)
n.festival_histories.first.awards
NO QUERY!
Without include option:
n = Nomination.first
Nomination Load (0.4ms) SELECT "nominations".* FROM "nominations" ORDER BY "nominations"."id" ASC LIMIT 1
n.festival_histories
FestivalHistory Load (25.5ms) SELECT "festival_histories".* FROM "festival_histories" WHERE "festival_histories"."nomination_id" = ? [["nomination_id", 1]]
n.festival_histories.first.awards
Award Load (0.7ms) SELECT "awards".* FROM "awards" WHERE "awards"."festival_history_id" = ? [["festival_history_id", 1]]
I think difference is obvious now:)
class Nomination < ActiveRecord::Base
belongs_to :festival
has_many :festival_histories, -> { includes :awards }
has_many :awards, through: :festival_histories
attr_accessible :name
end
Then you can call
Nomination.first.awards
Here's what's going wrong in you console, since festival_histories is an a collection of records, you cannot get the awards for a collection, only an individual record. So instead of
n = Nomination.first
n.festival_histories.awards
You need
n = Nomination.first
n.festival_histories.each { |r| puts r.awards}
to see the awards for each festival_history.
(So yes, how you are include: the :awards for lazy loading is working, and it's not a mistake in the documentation ;))
I have the following models, User, Dad, Mom, Follow and Kid.
class User
has_many :kids
has_many :follows
geocoded_by :address
end
class Dad
has_many :kids
has_many :follows
end
class Mom
has_many :kids
geocoded_by :address
end
class Follow
belongs_to :dad
end
class Kid
belongs_to :mom
belongs_to :dad
belongs_to :user
end
With the Geocoder gem I'm trying to create a scope that takes in the total count of the Dad's Kids nearest to the User's location compared to the Kids Mom's locations that happened today.
<% #follows.each do |f| %>
<% f.dad.kids.today.nearest.count
<% end %>
In my ApplicationController:
def set_user_location
if signed_in?
#user_location = current_user.address
end
end
In my Kid model:
scope :today, -> { where("kids.created_at > ?", Time.now.beginning_of_day) }
scope :nearest, -> { joins(:mom).merge(Mom.near(#user_location, 2000)) }
But the nearest doesn't work. It doesn't load Moms at all.
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 5 ORDER BY "users"."id" ASC LIMIT 1
Processing by FollowsController#index as HTML
Follow Exists (0.3ms) SELECT 1 AS one FROM "follows" WHERE "follows"."user_id" = $1 LIMIT 1 [["user_id", 5]]
Follow Load (0.5ms) SELECT "follows".* FROM "follows" INNER JOIN "dads" ON "dads"."id" = "follows"."dad_id" WHERE "follows"."user_id" = $1 ORDER BY name ASC LIMIT 30 OFFSET 0 [["user_id", 5]]
Product Load (0.2ms) SELECT "dads".* FROM "dads" WHERE "dads"."id" = $1 ORDER BY "dads"."id" ASC LIMIT 1 [["id", 39]]
(0.7ms) SELECT COUNT(*) FROM "kids" WHERE "kids"."dad_id" = $1 AND (kids.created_at > '2014-05-08 04:00:00.000000') [["dad_id", 39]]
CACHE (0.0ms) SELECT COUNT(*) FROM "kids" WHERE "kids"."dad_id" = $1 AND (kids.created_at > '2014-05-08 04:00:00.000000') [["dad_id", 39]]
What would be the correct way to write the nearest scope?
Problem is with today scope, not nearest.
Every model has sreated_at field so in your scope you should write
scope :today, -> { where("kids.created_at > ?", Time.now.beginning_of_day) }
EDIT:
Another error is that you're referring to controller instance variable in model. Model and controller are separated so it will not work this way. Instead you should rewrite scope to
scope :nearest, ->(location, distance=2000) { joins(:mom).merge(Mom.near(location, distance)) }
and then in the controller you may write
Kid.nearest(some location goes here)
but I think your models are a little messed up and you want them to do something different.
WhatI suppose you want to do is find a mother that is nearest to kid, so rather you should do something like
Mom.near(some_kid, 2000)
I'm trying to eager load results with 5 tables:
class Meeting < ActiveRecord::Base
has_many :bookings
end
class Booking < ActiveRecord::Base
belongs_to :user
belongs_to :meeting
belongs_to :role
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, through: :group_memberships
end
class GroupMembership < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, through: :group_memberships
end
Inside MeetingController.rb:
def index
#meetings = Meeting.where(year: #year)
end
I'm trying to use includes(:bookings) but according to bullet-gem the eager loading is not working. And inside my view I'm calling this line:
#meetings.each do |meeting|
meeting.bookings.find_by(group: #group, role: #role)
end
I'm getting multiple 52 lines of this code which clearly means that I'm not eager loading any data:
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."user_id" = 8 AND "bookings"."group_id" = 2 LIMIT 1 [["meeting_id", 207]]
Rendered shared/_book.html.haml (1.0ms)
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."group_id" = 2 AND "bookings"."role_id" = 4 LIMIT 1 [["meeting_id", 208]]
Booking Load (0.0ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."user_id" = 8 AND "bookings"."group_id" = 2 LIMIT 1 [["meeting_id", 208]]
Rendered shared/_book.html.haml (1.0ms)
Booking Load (0.1ms) SELECT "bookings".* FROM "bookings" WHERE "bookings"."meeting_id" = ? AND "bookings"."group_id" = 2 AND "bookings"."role_id" = 4 LIMIT 1 [["meeting_id", 209]]
bullet.log:
2014-03-29 15:30:48[WARN] user: karlingen
localhost:3000http://localhost:3000/meetings/host
Unused Eager Loading detected
Meeting => [:bookings]
Remove from your finder: :include => [:bookings]
Any ideas what I should be doing?
Try the following:
#meetings = Meeting.includes(:bookings).where(year: #year)
Also, how are you passing in the #year?
Meeting.includes(:bookings).where(year: #year).references(:bookings)
will work if you are using rails 4.