Maybe rookie problem, maybe not, maybe i am lack of OOT but I still can't answer why I cant get the the value of instance variable #upgrade which was assigned to true.
class OrderTemplate < ActiveRecord::Base
belongs_to :user
attr_writer :upgrade #to hold upgrade process across actions
def upgrade
#upgrade || false
end
def from_time
p self.inspect
------------------------> they looks same
p self.upgrade
------------------------> is true as is supposed to be
p self.user.order_template.inspect
------------------------> they looks same
p self.user.order_template.upgrade
------------------------> is false but i am expecting true
self.user.has_time_bonus?
end
end
class User < ActiveRecord::Base
has_one :order_template
def has_time_bonus?
p self.order_template.upgrade
------------------------> is false but i am expecting true
end
end
Please smack me.
The short version is "activerecord doesn't have an identity map" (or at least, it's not enabled). If you do
an_order_template.user.order_template
then user.order_template causes the OrderTemplate to be loaded from the database a second time, so you have two distinct in memory objects representing the same database row. The second copy won't have any in memory only changes (including your instance variable).
You can probably work around this by doing
class OrderTemplate < ActiveRecord::Base
belongs_to :user, :inverse_of => :order_template
end
class User < ActiveRecord::Base
has_one :order_template, :inverse_of => :user
end
the :inverse_of option helps Active Record join the dots so that when you do
an_order_template.user.order_template
rails knows that two order templates are the same object.
Related
I have a Lesson model which has many Completions like this:
class Lesson < ActiveRecord::Base
has_many :completions, as: :completable
belongs_to :course
end
And each Completion belongs to a User as well:
class Completion < ActiveRecord::Base
belongs_to :user
belongs_to :completable, polymorphic: true
end
From my application perspective I'm only interested in the amount of completions for a certain lesson, so I've included a counter cache. In regard to the individual Completions, I'm only interested if the Lesson is completed by the current user (I'm using Devise).
Is there some way to create a dynamic has_one relationship of some kind, that uses the information from the current_user to query the Completion table?
for instance:
has_one :completion do
def from_user current_user
Completion.where(completable: self, user: current_user)
end
end
Although this could work, I'm also having a polymorphic relationship. Rails is complaining that there's no foreign key called lesson_id. When I add a foreign_key: symbol, the do-end block stops working.
Any ideas?
Why not passing both block and options to has_many?
class Lesson < ActiveRecord::Base
has_many :completions, as: :completable do
def from_user user
if loaded?
find {|c| c.user_id = user.id}
else
find(user_id: user.id)
end
end
end
belongs_to :course
end
#lesson = Lesson.last
# Association not loaded - executing sql query
#lesson.completions.from_user(current_user)
#lesson.completions
# Association loaded - no sql query
#lesson.completions.from_user(current_user)
NOTE: You cannot treat it as an association, so it cannot be preloaded on its own.
I have 2 models: Dealer & Location.
class Dealer < AR::Base
has_many :locations
accepts_nested_attributes_for :locations
validate :should_has_one_default_location
private
def should_has_one_default_location
if locations.where(default: true).count != 0
errors.add(:base, "Should has exactly one default location")
end
end
end
class Location < AR::Base
# boolean attribute :default
belongs_to :dealer
end
As you understood, should_has_one_location adds error everytime, because .where(default: true) makes an sql query. How can I avoid this behaviour?
The very dirty solution is to use combination of inverse_of and select instead of where, but it seems very dirty. Any ideas?
I actually got an answer to a similar question of my own. For whatever it's worth, If you wanted to do a validation like you have above (but without the db query), you would do the following:
errors.add(:base, ""Should have exactly one default location") unless locations.any?{|location| location.default == 'true'}
I've been looking for this kind of functionality in AR, but don't seem able to find it. The Dirty implementation of AR states that an already persisted instance is deemed only dirty if one of its direct attributes has changed. So, let's say:
class Picture < ActiveRecord::Base
belongs_to :gallery
has_one :frame
end
in this case, I can do something like:
p = Picture.new(:gallery => Gallery.new, :frame => Frame.new)
p.save #=> it will save the three instances
p.gallery = Gallery.new
p.save #=> will not save the new gallery
p.gallery_id_will_change!
p.gallery = Gallery.new
p.save #=> this will save the new Gallery
but now I can't do something similar for the has_one association, since the Picture implementation doesn't own an attribute referring to it. So, it seems such dirty markups are impossible. Or aren't they?
Best thing I can figure out to do is:
class Picture < ActiveRecord::Base
belongs_to :gallery
has_one :frame
after_save :force_save_frame
def force_save_frame
frame.save! if frame.changed?
end
end
Like weexpectedTHIS said Dirty flags are set for attributes on the model itself, not for related objects. It only works for the belongs_to because there is a foreign key on the model.
But you can make a trick like that
module FrameDirtyStateTracking
def frame=(frame)
attribute_will_change!('frame')
super
end
end
class Picture < ActiveRecord::Base
prepend FrameDirtyStateTracking
belongs_to :gallery
has_one :frame
end
picture = Picture.new
picture.frame = Frame.new
picture.changed? # => true
picture.changed # => ['frame']
I feel a bit stupid but this is not working (and I expected it should work):
class MembershipCard < ActiveRecord::Base
belongs_to :association
belongs_to :personal_record
validates :number, :presence => true
def dis
print "---------------------------- #{personal_record.as_json}---------------------"
number
end
def value
id
end
end
class PersonalRecord < ActiveRecord::Base
has_many :membership_card, :dependent => :nullify
def dis
"#{name} #{surname}"
end
def val
id
end
end
print "---------------------------- #{personal_record.as_json}---------------------"
It's not printing. Any suggestion about why this is happening?
I can't access any associated model in this way and it's a disaster, basically I can't use activerecord.
I solved the problem by myself: it seems that association is used inside rails (damn me), expecially this was crashing my application. Commenting it solved the issue, so I'm going to rename models/controllers and so on.
I want to run some code before an object is removed from a has_many association.
I thought that I would be able to do this with the before_remove callback however for some reason this isn't firing and I don't understand why.
class Person < ActiveRecord::Base
has_many :limbs, before_remove: :print_message
def print_message
puts 'removing a limb'
end
end
class Limb < ActiveRecord::Base
belongs_to :person
end
While this code should print "removing a limb" during the destruction of a limb but it doesn't.
p = Person.create;
l = Limb.create person: p;
p.limbs.first.destroy
# SQL (2.1ms) DELETE FROM "limbs" WHERE "limbs"."id" = ? [["id", 5]]
# => #<Limb id: 5, person_id: 3, created_at: "2012-01-17 11:28:01", updated_at: "2012-01-17 11:28:01">
Why does this destroy action not cause the print_message method to run?
EDIT - does this before_remove callback exist?
A number of people have asked whether this callback exists. Although I can find very few further references to it, it is documented in the Rails documentation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Association+callbacks
It's an association callback though rather than a root ActiveRecord callback
Edit 2 - why not just use before_destroy on Limb?
Some people are asking why I'm not using the before_destroy callback on Limb. The reason is that I want person to check that there is a minimum number of limbs and that the last one is never destroyed. This is the original problem:
How do you ensure that has_many always "has a minimum"?
before_remove callback exists as an option in Associations callbacks. It's not the same as before_destroy, which is an ActiveRecord callback.
This is how you use it:
class Person < ActiveRecord::Base
has_many :limbs, :before_remove => :print_message
def print_message(limb)
# limb variable references to a Limb instance you're removing
# ( do some action here )
# ...
end
end
class Limb < ActiveRecord::Base
belongs_to :person
end
You're also calling a remove method incorrectly.
p = Person.create
l = Limb.create(:person => p)
p.limbs.first.destroy
Here you're calling it on Limb instance, that's why nothing is triggered.
Call it on an association you created:
p = Person.create
l = Limb.create(:person => p)
p.limbs.destroy(l)
EDIT
For preserving minimum of associated objects you can do something like this:
class Person < ActiveRecord::Base
has_many :limbs, :before_remove => :preserve_mimimum
def preserve_minimum(limb)
raise "Minimum must be preserved" if limbs.count == 1
end
end
class Limb < ActiveRecord::Base
belongs_to :person
end
This however does not get triggered on p.limbs.destroy_all, so you have to do something like this p.limbs.each {|l| p.limbs.destroy(l)}
Why it does not get triggered by destroy_all?
Because of this:
def destroy_all(conditions = nil)
find(:all, :conditions => conditions).each { |object| object.destroy }
end
It iterates over each element of an association and executes destroy action on an object and not on an association, that's why.
Replacebefore_remove with before_destroy.
Edit - handling minimum number of limbs
class Limb < ActiveRecord::Base
belongs_to :creature
before_destroy :count_limbs
def count_limbs
return false if self.creature.limbs.length <= self.creature.min_limbs
end
end
That return false will, I believe, stop it from being destroyed. Although I could be wrong
I can't say I've ever used the before_remove callback before, and not sure it exists.
The before destroy callback should be on the Limb model rather, and should look like this:
class Limb < ActiveRecord::Base
belongs_to :person
before_destroy :print_message
def print_message
puts 'removing a limb'
end
end