has_and_belongs_to_many and after_save problem - ruby-on-rails

I've got two models
class Payment < ActiveRecord::Base
has_and_belongs_to_many :invoices
after_save :update_invoices_state
def update_invoices_state
self.invoices.each{|i| i.update_state }
end
end
class Invoice < ActiveRecord::Base
has_and_belongs_to_many :payments
def pending_value
paid_value = Money.new(0,self.currency)
self.payments.each{|payment| paid_value += payment.value}
self.value - paid_value
end
def update_state
if self.pending_value.cents >= 0
if self.due_on >= Time.zone.today
new_state = :past_due_date
else
new_state = :pending
end
else
new_state = :paid
end
self.update_attribute :state, new_state
end
end
I've been debuggin this and I've found that when invoice.update_state is run self.payments is empty. Looks like HABTM hasn't been updated yet.
How could i solve this?

I believe HABTM has been mostly replaced by has_many :through.
You would create a join model, something like "InvoicePayment" (or something else creative)
class Payment < ActiveRecord::Base
has_many :invoice_payments
has_many :invoices, :through => :invoicepayments
end
class InvoicePayment < ActiveRecord::Base
belongs_to :invoice
belongs_to :payment
end
class Invoice < ActiveRecord::Base
has_many :invoice_payments
has_many :payments, :through => :invoice_payments
end
This should fix your problem.

Related

rails accepts_nested_attributes_for can't update

I Using accepts_nested_attributes_for to update has_many nested tables, Why not update but insert
diaries_controller.rb
def update
#diary=Diary.find(params[:id])
if #diary.update(update_diary_params)
render_ok
else
render_err :update_error
end
end
def update_diary_params
params.require(:diary).permit(:date,:weather,:remark, :diary_pictures_attributes=> [:diary_picture,:clothing_picture,:id,:_destroy])
end
model/diary.rb
class Diary < ApplicationRecord
has_many :diary_pictures,dependent: :destroy
accepts_nested_attributes_for :diary_pictures,allow_destroy: true
end
model/diary_picture.rb
class DiaryPicture < ApplicationRecord
belongs_to :diary
validates_presence_of :diary
end
enter image description here

Rails ActiveRecord sets object attribute nil when creating or initializing object even when value is present

When creating a model object active record sets the objects attribute to nil even if the values are set
#company.payment_orders.create(subscription_id: 1, price: 1000.0)
#<PaymentOrder id: nil, company_id: 1, subscription_id: nil, price: nil,..>
my models look some thing like this
class Company < ActiveRecord::Base
has_one :subscription
end
class PaymentOrder < ActiveRecord::Base
belongs_to :company
belongs_to :subscription
end
class Subscription < ActiveRecord::Base
has_many :payment_orders
end
And class for recording the payment_order
class PaymentOrderRecorder
def initialize(company, subscription, price)
#company = company
#subscription = subscription
#price = price
end
def record
#company.payment_orders.create(subscription_id: #subscription_id, price: #price)
end
....
end
I think you should use has_many through association for this:
class Company < ActiveRecord::Base
has_many :payment_orders
has_many :subscription, :through => :payment_orders
end
class PaymentOrder < ActiveRecord::Base
belongs_to :company
belongs_to :subscription
end
class Subscription < ActiveRecord::Base
has_many :payment_orders
has_many :companies, :through => :payment_orders
end
then perform this query:
#company.payment_orders.create(subscription_id: 1, price: 1000.0)
it will work
you don't have a relation, has_many :payment_orders in the company. Please add that and try
Models:
class Company < ActiveRecord::Base
has_one :subscription
has_many :payment_orders
end
class PaymentOrder < ActiveRecord::Base
belongs_to :company
belongs_to :subscription
end
class Subscription < ActiveRecord::Base
has_many :payment_orders
end
Then,
class PaymentOrderRecorder
.....
def record
#company.payment_orders.create(subscription_id: #subscription_id, amount: #price)
end
....
end

How to create new record via has_many through by Rails?

I am currently struggling with a has_many :through association in my project.
This is my model
class Group < ActiveRecord::Base
has_many :user_groups ,dependent: :destroy
has_many :users , through: :user_groups
end
class User < ActiveRecord::Base
has_many :user_groups ,dependent: :destroy
has_many :groups , through: :user_groups
end
class UserGroup < ActiveRecord::Base
belongs_to :user , inverse_of: :placements
belongs_to :group , inverse_of: :placements
validates :level , presence: true
end
So when i tried to create new group but it didn't work out.
This is my controller
class GroupController < ApplicationController
def create
group = Group.new(group_params)
group.users << User.find_by(id: current_user.id)
if group.save
render json: group, status: 201, location: [group]
else
render json: { errors: group.errors }, status: 422
end
end
private
def group_params
params.require(:group).permit(:name, :shuttle_price, :court_price)
end
end
But when i call create method i got this error.
Could not find the inverse association for group (:placements in Group)
On this line
group.users << User.find_by(id: 6)
So how can i fix this?
Thanks!
Remove :inverse_of
class UserGroup < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates :level , presence: true
end
You don't need to add inverse_of there. read this when to use inverse_of

Updating child model with same data as parent

I have four models related to each other as below:
class User < ActiveRecord::Base
has_many :clients
has_many :default_prices
end
class DefaultPrice < ActiveRecord::Base
has_many :client_prices
has_many :clients, :through => :user
belongs_to :user
end
class Client < ActiveRecord::Base
belongs_to :user
has_many :client_prices
before_create do
user.default_prices.each do |default_price|
client_prices.build("price" => default_price.price, "visit_type" => default_price.visit_type, "default_price_id" => default_price.id)
end
end
end
class ClientPrice < ActiveRecord::Base
belongs_to :client
belongs_to :default_price
end
Right now when a new client is created by the user, the user's default prices are applied to the client's client_prices table. How can I have new client_prices (for each existing client) created when new default_prices are created by the user? Also, how can I have the client_prices update when the default prices are changed? Each client prices has an default_price_id column that relates to the default price, if that helps.
class DefaultPrice < ActiveRecord::Base
before_create :new_client_price
before_update :update_clients_price
private
def new_client_price
clients.each do |c|
self.client_prices.create(:price => self.price, :visit_type => self.visit_type, :client_id => c.id)
end
end
def update_clients_price
self.client_prices.each do |p|
p.price = self.price
p.visit_type = self.visit_type
p.save
end
end

How to know when the model is destoyed automatically by a :dependent => :destroy in rails?

I have the following association:
class Parent < ActiveRecord::Base
has_many :children, :dependent => :destroy
before_destroy :do_some_stuff
end
class Child < ActiveRecord::Base
belongs_to :parent
before_destroy :do_other_stuff
end
I would like to know in do_other_stuff if the destruction has been fired by dependent => destroy or not because part of it would/will be done in do_some_stuff
I tried parent.destroyed?, parent.marked_for_destruction?, parent.frozen? but nothing work :/
any ideas?
You can use the association callbacks ( before_remove or after_remove)
class Parent < ActiveRecord::Base
has_many :children, :dependent => :destroy, :before_remove => :do_foo
before_destroy :do_bar
def do_bar
end
def do_foo
end
end
Maybe something like that:
class Parent < ActiveRecord::Base
has_many :children
before_destroy :some_stuff
def some_stuff
children.each do |child|
child.parent_say_bye
end
end
end
class Child < ActiveRecord::Base
belongs_to :parent
before_destroy :do_other_stuff
def parent_say_bye
#do some stuff
delete
end
end

Resources