I have two models (lets call then A and B).
A has_many bs and B belongs_to A.
class A < ApplicationRecord
has_many :bs, dependent: :destroy, inverse_of: :a
accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
validates_associated :bs
end
class B < ApplicationRecord
belongs_to :a, inverse_of: :bs
before_update :do_something, unless: Proc.new { |b| b.a.some_enum_value? if a }
def do_something
self.some_field = nil
end
end
Other than that, B has a before_update callback that sets some_field to nil if A has some_enum_value set.
Since this relation is used on a nested form, that before_update from B is only being called if I update a attribute form B. If I only change a value form A that callback is not called.
How can I call B's before_update when A is updated?
Thanks in advance.
For belongs to associations you can use the touch option:
class B < ApplicationRecord
belongs_to :a, inverse_of: :bs, touch: true
end
Which would update a.updated_at when you update B.
However this option does not exist for has_many relations since it could have potentially disastrous performance consequences (If an A has 1000s or more Bs).
You can roll your own however:
class A < ApplicationRecord
has_many :bs, dependent: :destroy, inverse_of: :a
accepts_nested_attributes_for :bs, reject_if: :all_blank, allow_destroy: true
validates_associated :bs
after_update :cascade_update!
def cascade_update!
# http://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each
bs.find_each(batch_size: 100) do |b|
b.update!(updated_at: Time.zone.now)
end
end
end
Related
I'm trying to make a method that gets the date and adds onto it an amount of days specified.
At present I cannot call the days specified.
I have a Plant Class that has_many DaysTillSellables, The Period class has_many DaysTillSellable also.
When the user creates a plant they can add a DaysTillSellables and then select a period and then enter an amount of days.
I first need to check to see which period the date is in, then return that period. Currently attempting like so
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
Then Find the days till sellable that is connected to that period and finally call the days from that
Below is the code for the class I'm trying to call it in
class Potting < ApplicationRecord
belongs_to :batch, inverse_of: :pottings
validates :plant_count, :completed_at, presence: true
enum germination_result: { light: 0, medium: 1, full: 2 }
def pottings_completed_at
"Week #{completed_at.strftime('%U').to_i}/#{completed_at.strftime('%Y').to_i}"
end
def completed_at
super || Time.zone.today
end
def completed_at_week
completed_at.strftime('%U')
end
def current_period
return unless completed_at_week.between?(period.start_week, period.finish_week)
index_days_till_sellables_on_period_id
end
def period_days
plant.days_till_sellables.find_by(period: :current_period).¤t_period.days
end
def ready_for_sale
completed_at + period_days
end
end
I've added more Code below to give better context for classes
class DaysTillSellable < ApplicationRecord
belongs_to :period, inverse_of: :days_till_sellables, foreign_key: :period_id
belongs_to :plant, inverse_of: :days_till_sellables, foreign_key: :plant_id
validates :days, presence: true
end
.
class Period < ApplicationRecord
has_many :days_till_sellables, dependent: :destroy, inverse_of: :period
belongs_to :organization
.
class Plant < ApplicationRecord
has_many :batches, dependent: :destroy
has_many :goals, dependent: :destroy
has_many :days_till_sellables, dependent: :destroy, inverse_of: :plant
belongs_to :organization
accepts_nested_attributes_for :days_till_sellables, allow_destroy: true
validates :genus, :species, :period_id, presence: true
end
I think you are looking for:
class Potting
belongs_to :plant, through: :batch
...
end
I have 3 models
HiringTag
class HiringTag < ActiveRecord::Base
attr_accessible :name, :staffroom_id
validates :name, presence: true
has_many :hiring_tag_applications
has_many :job_applications, through: :hiring_tag_applications
after_destroy { |application|
HiringTagApplication.destroy(application.job_applications.pluck(:job_application_id))
end
HiringTagApplication
class HiringTagApplication < ActiveRecord::Base
belongs_to :hiring_tag, dependent: :destroy
belongs_to :job_application
end
JobApplication
class JobApplication < ActiveRecord::Base
has_many :hiring_tag_applications
has_many :hiring_tags, through: :hiring_tag_applications
end
What I am trying to do: is when I destroy the HiringTag or JobApplication, I want the related data to be deleted in HiringTagApplication as you will notice I have a after_destroy inside HiringTag that call back does get executed but the error I get is:
Couldn't find HiringTagApplication with id=96453
96453 is not the id of HiringTagApplication but it is job_application_id
How can I correct this so the record can be deleted?
No need of after_destroy, just use dependent: :destroy correctly like:
class HiringTag < ActiveRecord::Base
has_many :hiring_tag_applications, dependent: :destroy
end
And
class JobApplication < ActiveRecord::Base
has_many :hiring_tag_applications, dependent: :destroy
end
& remove it from associated table
class HiringTagApplication < ActiveRecord::Base
belongs_to :hiring_tag
belongs_to :job_application
end
Now, whenever HiringTag or JobApplication gets deleted its associated HiringTagApplication will also be deleted
I have a table with cells.
Using cocoon how would I make a form in which each cell belongs_to the row, column, and table?
For Example:
# Table
class Roster < ApplicationRecord
has_many :timeslots, inverse_of: :roster
has_many :games, inverse_of: :roster
accepts_nested_attributes_for :timeslots, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :games, reject_if: :all_blank, allow_destroy: true
end
# Column
class Court < ApplicationRecord
belongs_to :roster
has_many :games
accepts_nested_attributes_for :games, reject_if: :all_blank, allow_destroy: true
end
# Row
class Timeslot < ApplicationRecord
belongs_to :roster
has_many :games
accepts_nested_attributes_for :games, reject_if: :all_blank, allow_destroy: true
end
# Cell
class Game < ApplicationRecord
belongs_to :timeslot
belongs_to :roster
end
I am trying now with a hidden <input> for each game's :timeslot_id and :court_id the only problem is you cannot get the id before the timeslot and court is saved. The other idea I ma working on is to for each game to have a hidden <input> of the row/column they are in.
I finally figured it out:
Each cell had two hidden inputs:
<%= cell.hidden_field :row %>
<%= cell.hidden_field :column %>
The cell belongs_to association had to be optional:
class Cell < ApplicationRecord
belongs_to :table
belongs_to :column, optional: true
belongs_to :row, optional: true
end
And the Table had an after_save callback:
after_save do |table|
table.rows.each_with_index do |row,row_number|
table.cells.each do |cell|
if cell.row-1 == row_number
cell.row = row
cell.save()
end
end
end
table.columns.each_with_index do |column,column_number|
table.cells.each do |cell|
if cell.column-1 == column_number
cell.column = column
cell.save()
end
end
end
end
There is probably a better way to do this but I think this is the simplest.
You will need to add an extra two columns to your database: row and column (The main drawback)
I'm wondering if there is a cleaner way to validate multiple relationships in rails. I don't mean validating an association, rather ensuring relationship integrity between two or more belongs_to associations. Here is some code as an example:
class User < ActiveRecord::Base
has_many :products, inverse_of: :user
has_many :purchases, inverse_of: :user
end
class Purchase < ActiveRecord::Base
belongs_to :user, inverse_of: :purchases
has_many :products, inverse_of: :purchase
end
class Product < ActiveRecord::Base
belongs_to :user, inverse_of: :products
belongs_to :purchase, inverse_of: :products
validates :user, :purchase, presence: true
validate :purchase_user
private
def purchase_user
errors.add(:purchase, 'purchase user does not match user') if user != purchase.user
end
end
The purchase_user validation method here checks that the user is the same as purchase.user and adds an error if they are not.
I could change it to use :inclusion like so:
validates :purchase, inclusion: { in: proc { |record| record.user.purchases } }
But that seems even more inefficient, any suggestions?
Here's a simple Message model that i've set up.
class Message < ActiveRecord::Base
validates_presence_of :user_id, :head, :text
belongs_to :user
has_many :receivers, class_name: MessageReceiver
has_many :users, through: :receivers
before_destroy :check_receivers
private
def check_receivers
if self.receivers.empty?
true
else
self.update(deleted: 1)
false
end
end
end
The :check_receivers method works as expected when called directly (I remove it from the private section for that ofcource), but somehow it does not prevent a message from being destroyed.
Am I doing something wrong here?
Remove
before_destroy :check_receivers
and try
has_many :receivers, class_name: MessageReceiver, dependent: :restrict_with_exception