I have this uniqueness validation in my model:
validates_uniqueness_of :source_id,
scope: [:year_id],
conditions: -> { where(comment_type_id: CommentType.final.id) },
message: %(
There can be only one final comment per year
)
I have 2 types of comment, the 'CommentType.internal' which can be added to the commentary table many times, and 'CommentType.final' which can be saved once, but via update action it can be modified.
I'm able to save the one record with the final comment_type_id, but when I want to create another regardless of which commentary_type_id I have it still fails on this uniqueness validation.
What am I doing wrong?
This worked for me, I ll answer for anyone else facing the same issue :
validates_uniqueness_of :comment_type_id,
scope: [:year_id, :source_id],
if: ->(cmt) { cmt.comment_type_id == CommentType.final.id) },
message: %(
There can be only one final comment per year
)
Related
I have been struggling with a Client request to filter out A model(Candidate) on the basis of Column Format
The issue I am facing is that column inputs either SSN or EIN.
The format for SSN is (xxx-xx-xxxx)
The format for EIN is (xx-xxxxxxx)
My candidates Table contains the field ssn_or_ein , which takes either one of these 2.
For ex:
candidate111.ssn_or_ein => 111-11-1111
candidate222.ssn_or_ein => 22-2222222
I have tried fetching all the 4000 accounts, but i suppose that's not how a developer's approach should be.
I am still learning Rails and any tip would be really helpful.
You can do this with a like query. Put it in a scope so it's easily available.
class Candidate < ApplicationRecord
scope with_ein -> { where( "ssn_or_ein like ?", "__-_______" }
scope with_ssn -> { where( "ssn_or_ein like ?", "___-__-____" }
end
However, this can get slow if ssn_or_ein is not properly indexed.
Consider storing them in two different columns. This makes validation and querying simpler. Bring them together only when you just need a TIN - Taxpayer Information Number.
class Candidate < ApplicationRecord
scope with_ein -> { where.not( ein: nil ) }
scope with_ssn -> { where.not( ssn: nil ) }
EIN_RE = %r{^\d{2}-\d{7}$}
SSN_RE = %r{^\d{3}-\d{2}-\d{4}$}
validates :ein, format: { with: EIN_RE }, allow_nil: true
validates :ssn, format: { with: SSN_RE }, allow_nil: true
def tin
ssn || ein
end
class << self
def find_by_tin(tin)
where( ssn: tin ).or( where(ein: tin) )
end
end
end
I would also suggest that you store the data "normalized", without the dashes, just the numbers. This is simpler to work with, and the accepted format can be changed without having to change all the data. Format them in a decorator.
I have some query, like a :
Assignment.where(date: "2019-07-01")
.includes(crews: :truck)
.where.not({ crews: {truck_id: nil}})
.count
#=> 2 records.
Here's an example with contain:
Assignment.where(date: "2019-07-01")
.includes(crews: :truck)
.where.not({ crews: {truck_id: nil}})
.distinct.pluck('trucks.name')
#=> ["Mercedes", "BMW"]
I want to check for the uniqueness of truck_id in Assignment.where(date: "2019-07-01")
And in order to attach an existing truck_id to another Assignment object, validation was triggered.
And a message pops up that you cannot add, since such a Truck object is already attached to one of the Assignment on the day that is specified in .where
Please tell me how to implement it correctly. Thank.
If you don't want to write a validate method, you can still use uniqueness options.Use conditional option to tell which rows should do the validation.
validates :truck_id, uniqueness: true, if: Proc.new { |assignment| assignment.on_specified_day? }
I'm kinda struggling with validations in my Rails application.
I have the following Setup:
class MealDay < ApplicationRecord
belongs_to :meal
belongs_to :day
has_many :meal_day_canteens
has_many :canteens,
through: :meal_day_canteens
validates :meal_id, uniqueness: {scope: :day_id}
end
#MainController
helping_hash.each { |days, meal|
dayRef = Day.where(date: days).first
mealRef = Meal.where(name: meal)
dayRef.meals << mealRef #This line is obviously throwing the error because a record exists already
}
The Error is: "Validation failed: Meal has already been taken"
But I'm not sure on how to handle the error. I just want it, so that Rails is not inserting it into the database and skips it. If you need more information just tell me.
Thanks.
Edit: Some more code which I can't get to work now.
Edit2: Forgot to add validation to that model. Works fine now
helping_hash.each { |days, meal|
dayRef = Day.where(date: days).first
mealRef = Meal.where(name: meal)
meal_day_ref = MealDay.where(day: dayRef, meal: mealRef)
#canteenNameRef.meal_days << meal_day_ref rescue next
}
How about rescueing the error?
helping_hash.each do |days, meal|
dayRef = Day.where(date: days).first
mealRef = Meal.where(name: meal)
dayRef.meals << mealRef rescue next
end
Rails uniqueness constraint is basically throwing a query under the hood to look for a record in the DB. More of a side comment but this is already not safe concurrently so I recommend you adding a constraint at the database level.
You basically need to do the manual work of skipping the ones that already exist.
Basically something like:
helping_hash.each do |day, meal|
day_ref = Day.find_by!(date: day)
meal_ref = Meal.find_by!(name: meal)
unless day_ref.meal_ids.include?(meal_ref.id)
# Depending on the number of records you expect
# using day_ref.meals.exists?(id: meal_ref.id) may be a better choice
day_ref.meals << meal_ref
end
end
I have a SapEvento model which belongs to SapIntegracao model. On SapEvento, I have the following validation:
validates :params_operacao, uniqueness: true, if: :check_params
def check_params
self.sap_integracao_log.recebimento
end
I'm doing that to prevent duplicate requests on a soap integration (sometimes the same xml comes multiple times). So the params I receive are saved on that :params_operacao attribute, as a string. But the problem is the validation in saving. When I receive the XML and create the SapEvento object by doing
evento_erro = sap_integracao_log.sap_eventos.create(
evento_tipo: SapEvento.get_evento_tipo("Erro na Integração"),
params_operacao: params.to_s,
erro_descricao: (!transporte ? "Transporte não encontrado" : erro),
reenviado: false,
operacao: operacao
)
it doesn't validate and permits to create another object, even if already exists an object with same :params_operacao value.
I debugged on check_params method:
logger.debug "recebimento #{self.sap_integracao_log.recebimento}"
logger.debug "count #{SapEvento.where(params_operacao: self.params_operacao).count}"
and recebimento is true and the count is bigger than 0... so it shouldn't permitt, right?
I also tried some other syntaxes, like:
validates :params_operacao, uniqueness: true, if: "self.sap_integracao_log.recebimento"
but none of these worked. Any ideas?
EDIT:
I also tried, on console, take the last SapEvento.params_operacao and create a new SapEvento doing SapEvento.new(params_operacao: last_evento_params_operacao) and save. It also doesn't give me any errors...
My model, Goal, has these three attributes: goal_type, goal_length, and is_complete.
Goal belongs_to :user.
goal_type can only be "X", "Y", or "Z"
goal_length can only be "short", "mid", or "long"
is_complete can be true or false
I want there to only ever be one Goal (validates_uniqueness_of) with a specified goal_length and goal_type when looking at Goals that both belong to the same user and have is_complete set to false
Plain english examples:
If a user has a short X goal that they have not yet completed, they cannot create a new short X goal.
If a user has a short X goal that they have completed, they can create a new short X goal.
This validation works, but does not check if the existing goals have their is_complete attribute set to true. If is_complete is true on the previously existing models, it is ok to create a new goal with the same attributes:
validates_uniqueness_of :goal_type, scope: [:goal_length, :user_id]
How would I add a check for the condition where the validation is ignored if the matching previous goal has is_complete set to true ?
This validation appears to work as expected:
validates_uniqueness_of :goal_type, scope: [:goal_length, :user_id], conditions: -> { where(is_complete: false) }