There have 2 tables: Orders and Arrivals. There can be many arrivals on an order. I want to validate the creation of arrivals for a specific order.
Orders has fields book_id and quantity:integer
Arrivals has fields order:belongs_to and quantity:integer
Order.rb:
class Order < ActiveRecord::Base
has_many :arrivals
def total_arrival_quantity
arrivals.map(&:quantity).sum
end
def order_quantity_minus_arrival_quantity
quantity - total_arrival_quantity
end
end
Arrival.rb:
class Arrival < ActiveRecord::Base
belongs_to :order
validates :total_arrival_quantity_less_or_equal_to_order_quantity, on: create
validates :current_arrival_quantity_less_or_equal_to_order_quantity, on: create
def current_arrival_quantity_less_or_equal_to_order_quantity
self.quantity <= order.quantity
end
end
How can I make the two validations work?
Something like this should work,
validate :order_quantity, on: :create
private
def order_quantity
if quantity > order.order_quantity_minus_arrival_quantity
errors.add(:quantity, 'cannot be greater than ordered quantity.')
end
end
Related
I have 3 models in my rails application, User, Course, and CourseTemplate.
A Course belongs to a User and a CourseTemplate belongs to a Course.
What I want to do is to validate the uniqueness between the CourseTemplate name and the User id.
Is this possible?
Without denormalization of data
class CourseTemplate < ActiveRecord::Base
belongs_to :course
has_one :user, through: :course
validate :unique_course_template_for_user
private
def unique_course_template_for_user
errors.add(:name, 'Must be unique') if CourseTemplate.find_by(user: user.id, name: self.name).count > 0
end
end
With denormalization of data
If you're ok with some denormalization of your data, you could add user_id to CourseTemplate, and then simply use the scope feature of validates uniqueness.
Below I show how to use callbacks to maintain the user_id in the CourseTemplate. Note that it assumes a course cannot be moved to a different user.
class CourseTemplate < ActiveRecord::Base
before_create :copy_user_id_from_course
validates :name, uniqueness: { scope: :user_id, message: 'Must be unique for the same user'}
private
def copy_user_id_from_course
self.user_id = course.user_id
true
end
end
If the course can be moved to a different user, you should add a callback on Course:
class Course < ActiveRecord::Base
after_save :set_course_templates_user, if: :user_id_changed?
private
def set_course_templates_user
course_templates.update_all user_id: self.user_id
end
end
I've app where I've got Ingredients and also have deliveries of Ingredients, after delivery ingredient is increasing value. I have a problem to find Ingredient before save.
On my delivery I store ingredient_id so I should have a in my controller function to find Ingredient via
before_save :find_ingredient
private
def find_ingredient
self.find_ingredient = find(:ingredient_id)
end
How to find this Ingredient before_save?
In my controller
if #delivery.save
#ingredient.quantity += #delivery.unloaded
#ingredient.save
Try this:
private
def find_ingredient
Ingredient.find(:ingredient_id).increment!(:quantity)
end
You'd do something like this:
#app/models/delivery.rb
class Delivery < ActiveRecord::Base
belongs_to :ingredient, inverse_of: :deliveries
before_save :increase_ingredient
private
def increase_ingredient
ingredient.increment! :quantity, unloaded
end
end
I have two tables, Member and MemberRecord.
This are their relationship:
# Member Model
class Member < ActiveRecord::Base
has_many :member_records, :dependent => :destroy
end
# MemberRecord Model
class MemberRecord < ActiveRecord::Base
belongs_to :member
end
In MemberRecord There are many columns: two_pointer_attempt, two_pointer_made, three_pointer_attempt, three_pointer_made, free_throws_attempt, free_throws_made, offensive_rebound, defensive_rebound, assist, block, steal, turnover, foul, score
Can I get those columns sum in more efficient way?
This is what I did so far:
class Member < ActiveRecord::Base
belongs_to :team
has_many :member_records, :dependent => :destroy
validates :name, :number, presence: true
validates_uniqueness_of :name, scope: :team_id
validates_inclusion_of :number, in: 0..99
def sum_two_pointer_made
self.member_records.sum(:two_pointer_made)
end
def sum_two_pointer_attempt
self.member_records.sum(:two_pointer_attempt)
end
def sum_two_pointer_total
sum_two_pointer_made + sum_two_pointer_attempt
end
def sum_three_pointer_made
self.member_records.sum(:three_pointer_made)
end
def sum_three_pointer_attempt
self.member_records.sum(:three_pointer_attempt)
end
def sum_three_pointer_total
sum_three_pointer_made + sum_three_pointer_attempt
end
def sum_free_throws_made
self.member_records.sum(:free_throws_made)
end
def sum_free_throws_attempt
self.member_records.sum(:free_throws_attempt)
end
def sum_free_throws_total
sum_free_throws_made + sum_free_throws_attempt
end
def sum_offensive_rebound
self.member_records.sum(:offensive_rebound)
end
def sum_defensive_rebound
self.member_records.sum(:defensive_rebound)
end
def sum_assist
self.member_records.sum(:assist)
end
def sum_block
self.member_records.sum(:block)
end
def sum_steal
self.member_records.sum(:steal)
end
def sum_turnover
self.member_records.sum(:turnover)
end
def sum_foul
self.member_records.sum(:foul)
end
def sum_score
self.member_records.sum(:score)
end
end
I will give you an example with two columns and you can extend it for your number of columns.
class Member < ActiveRecord::Base
# add associations here as already present
MR_SUM_COLUMNS = %w{
assist
block
} # add more member record columns here
MR_SUM_COLUMNS.each do |column|
define_method "member_record_#{column}_sum" do
member_record_sums.send(column)
end
end
private
def member_record_sums
#_member_record_sums ||=
begin
tn = MemberRecord.table_name
sums_str =
MR_SUM_COLUMNS.map do |c|
"SUM(#{tn}.#{c}) AS #{c}"
end.join(', ')
self.member_records.select(sums_str).first
end
end
end
m = Member.first
s1 = m.member_record_assist_sum
s2 = m.member_record_block_sum
Explanation:
In ActiveRecord's select method, you can store the sum of a column as a particular value. For example:
# you have only two members with ids 1 and 2
m = Member.select("SUM(id) AS id_sum").first
m.id_sum #=> 3
So we're storing all sums of member_records in one go: in the member_record_sums method. We are also using an instance variable to store the results so that subsequent calls to the method do not query the database.
From there, all we have to do is define our sum-lookup methods dynamically.
I have a Model Reservation with 2 columns, user_id and teleporter_id.
I want to lock the creation of a Reservation to 3 same teleporter_id but I don't know how to access to the attribute teleporter_id from the Model that I'm creation.
Here my code :
class Reservation < ActiveRecord::Base
# Relations
belongs_to :teleporter
belongs_to :user
# Validations
validate :max_reservation
def max_reservation
if Reservation.where(:teleporter_id => self.teleporter_id).count >= 3
errors.add(:base, t('reservation.model.error.max_reservation'))
end
end
end
I think that the problem is from self.teleporter_id but I don't know how access to the attribut teleporter_id from the current model.
Try this:
def max_reservation
_id = self.teleporter_id
errors.add(:base, t('reservation.model.error.max_reservation')) unless Reservation.where(teleporter_id: _id).count <= 3
end
Given this:
class User < ActiveRecord::Base
has_many :photos
MAX_PHOTOS = 5
validate :quantity_of_photos
def quantity_of_photos
???
end
end
And this:
#user.photos.size # => 5
I need this to fail:
#user.photos << Photo.create(valid_photo_attributes)
How do I do this validation?
Move the quantity of photos method to the Photo model:
class Photo < ActiveRecord::Base
belongs_to :user
validates :quantity_of_photos
def quantity_of_photos
if new_record? && user.photos.size >= User::MAX_PHOTOS
errors.add_to_base "You cannot have more than #{MAX_PHOTOS} photos."
end
end
end
The validity of a ActiveRecord instance is determined by whether there are errors in it's errors array.