How to get parent from child in nested model rails 6 - ruby-on-rails

I have issue when try to get parent model from child as below:
Post model
class Post < ApplicationRecord
has_many :post_votes, dependent: :destroy
accepts_nested_attributes_for :post_votes
end
PostVote model
class PostVote < ApplicationRecord
belongs_to :post, optional: true
end
Get parent funtion
def find_owner_post_vote_for_user(user)
#owner_post = Post.first
#owner_post_vote = PostVote.first
if user.id.present?
#owner_post_vote = PostVote.where(user_id: user.id)
#owner_post = #owner_post_vote.post
end
return #owner_post
end
Error log:
ActionView::Template::Error (undefined method `post' for #<PostVote::ActiveRecord_Relation:0x00007fffc23f9c78>):
Get child model #post.post_votes is OK, but get parent model is false.
Any can help me fix this problem? Thank so much!

PostVote.where(user_id: user.id) will return a collection of records. So, on the very next line when you do #owner_post_vote.post It's trying to find a post for a collection of records ActiveRecord_Relation.
if you want to use where, then you should iterate through the collection. Or, you can you find to get a single record. Then you can do #owner_post_vote.post.
Option 1:
if user.id.present?
#owner_post_vote = PostVote.find(user_id: user.id)
#owner_post = #owner_post_vote.post
end
Option 2:
if user.id.present?
#owner_post_vote = PostVote.where(user_id: user.id).first
#owner_post = #owner_post_vote.post
end
Option 3:
if user.id.present?
#owner_post_votes = PostVote.where(user_id: user.id)
#owner_post_votes.each do |post_vote|
# post_vote.post is accessible here
end
end

Related

Can't access prop in has_one relationship

I have two models:
class Post < ApplicationRecord
has_one :metric, dependent: :destroy
end
class Metric < ApplicationRecord
belongs_to :post
end
And for some reason that I don't fully understand, I can't create a new metric through a post:
> post = Post.first 1
> post.metric # => Nil
> post.metric.create # NoMethodError (undefined method `create' for nil:NilClass)
Do I have to declare anything else any of my models to make this work?
You can't call create method on nil class.
Active Record Associations DOC
post = Post.first 1
post.build_metric # will be automatically assigned post_id to metrics but not saved
post.save
#or
post.create_metric # will be automatically assigned post_id to metrics and saved
Alternate solution:
Metric.create(post_id: post.id)
What you're doing is
post = Post.first 1
# You don't tell us what this evaluates to, but I'm assuming a post as the #metric call works
post.metric # => Nil
# You have nil
post.metric.create
# Now you're calling the create method on nil.
What you need to do is
Metric.create(post_id: Post.first.id, etc_attribute: etc_value, ...)
EDIT: Also what 7urkm3n said in his comment-- build_metric and create_metric are cleaner solutions that utilize Rails magic.

after_save callback of act_as_nested_set model lead to SystemStackError

I am using Rails 5.1.6
I have a model called Taxon using acts_as_nested_set. I have 4 levels of Taxons, the last level sub_category has an attribute holding names of all parents, I want to update the sub_category attribute every time any of its parents name is changed, when using after_save callback it runs into SystemStackError as each after save callback is run for each child leading to infinite loop. Any idea how to overcome this issue?
class Taxon
acts_as_nested_set dependent: :destroy
def update_tree_name
if shop_sub_category?
update(display_tree_name: beautiful_name)
else
related_sub_categories = tree_list.select{ |taxon| taxon.kind == "sub_category" }
related_sub_categories.each do |t|
t.update(display_tree_name: t.beautiful_name)
end
end
end
def beautiful_name
"#{parent.parent.parent.name} -> #{parent.parent.name} -> #{parent.name}-> #{name}"
end
I have a solution that will work for you but I do not think it is an elegant one but here you go and then you can fine tune it:
In your model:
class Taxon < ActiveRecord::Base
cattr_accessor :skip_callbacks
after_save :update_tree_name, :unless => :skip_callbacks
end
def update_tree_name
if shop_sub_category?
update(display_tree_name: beautiful_name)
else
related_sub_categories = tree_list.select{ |taxon| taxon.kind == "sub_category" }
Taxon.skip_callbacks = true # disable the after_save callback so that you do not end up in infinite loop (stackoverflow)
related_sub_categories.each do |t|
t.update(display_tree_name: t.beautiful_name)
end
Taxon.skip_callbacks = false # enable callbacks again when you finish
end
end

Referencing Associated Object Returns Nil

I have two models.
class User < ActiveRecord::Base
has_one :message
end
class Message < ActiveRecord::Base
belongs_to :user
end
If I have a created user with an associated Message and I delete that message and create a new one like, user.message returns nil. For example.
user = User.create
message = Message.create(user_id: user.id)
Message.where(user_id: user.id).destroy_all
Message.create(user_id: user.id)
# Now if I call this below, it always returns nil
user.message
Why does this occur? Shouldn't Rails 3 pick up on that change? How do I fix this?
Just load the object again before doing user.message like, user.reload.
reload - Reloads the record from the database.

rspec expect to change record value after create it through post

I have this piece of example to create a new record through post which is passed
describe 'POST create' do
let(:schedule_child) { FactoryGirl.create(:schedule_child) }
let(:post_queue) { post :create, schedule_child_id: schedule_child.id, format: :js }
it { expect{post_queue}.to change(PatientQueue, :count).by(1) }
end
And I have one attribute, PatientQueue.queue_number, which will be increased by 1 every time a new record is added. Now I'd like to see if this attributes has changed.
it { expect{post_queue}.to change(PatientQueue, :queue_number).by(1) }
But here is what I got
NoMethodError: undefined method `queue_number' for #<Class:0x0000000849e780>
How should I write it properly?
== UPDATE ==
model PatientQueue
class PatientQueue < ActiveRecord::Base
# Validations
validates :patient, :schedule_child, presence: true
validate :is_not_exist
# Relations
belongs_to :schedule_child
belongs_to :patient
before_create :insert_queue_number
def is_exist?
PatientQueue.find_by_schedule_child_id_and_patient_id(schedule_child_id, patient_id).present?
end
private
def insert_queue_number
last_id = PatientQueue.where("schedule_child_id = ?", self.schedule_child_id).count
self.queue_number = last_id + 1
end
def is_not_exist
errors.add(:schedule_child, :is_exist) if is_exist?
end
end
PatientQueue is an activerecord class, which has a method count
post_queue is an instance of the class and has the method queue_number
the class does not have the same methods as the instance, so you might write your test like change(post_queue, :queue_number).by(1)
However, the test is a little hard to follow, can you show us your data model relationships? if a PatientQueue has_many schedule_child, maybe you just want to use rails cache_counter? http://www.elegantruby.com/Tutorials/2013/01/25/adding-a-counter-cache-for-fun-and-profit/

Is possible to append a key value to an ActiveRecord object

I want to have an object that has an associated value added to it.
This is what I am attempting to do:
#users = #search.results
#user_results = []
#users.each do |user|
#user_results = #user_results.push user << {photo_url: UsersPhoto.find_by_user_id(user.id).image_name}
end
I'm getting:
NoMethodError (undefined method `<<' for #):
Is there a way to do something like this?
This is what associations are for. You should just add an association to your User for the UsersPhoto you're trying to find, and use #user.users_photo.image_name.
class User < ActiveRecord::Base
has_one :users_photo
end
Failing that, add a photo_url method to your User model, which wraps up the UsersPhoto.find...:
class User < ActiveRecord::Base
def photo_url
UsersPhoto.find_by_user_id(id).image_name
end
end
Failing that, you can do what you're trying to do, but you'll need to add a attr_accessor :photo_url to your User model, and your syntax is all wrong:
class User < ActiveRecord::Base
attr_accessor :photo_url
end
#users.each do |user|
user.photo_url = UsersPhoto.find_by_user_id(user.id).image_name
end
Why don't you use the .pluck method?
#users = #search.results
#user_results = UsersPhoto.where(user_id: #users.pluck(:id) ).select(:image_name)
To explain, at least from what I am understanding, you want an array of image_names for the results #users. So just get their ids, find those UsersPhotos that match those ids and just get the image_name column

Resources