Rails validation - maximum database entries - ruby-on-rails

I have a rails project, using active record and was wondering if there was a validation helper for max number of individual entries. For example, if you had a form submission and only wanted, say 2, and you just wanted the first 2 to be persisted to the table, how would you do this?
I have read the manual and had a look at numericality etc but it's not really what I'm looking for. I have tried to write my own validation method in the model, but I am assuming there is a validation helper that makes this easier:
def validatePassengerNumber
if self.passengers.length > 2
# unsure on how to refuse new data access to database
end
end

Add an error to base after check return true, and it will prohibit to save into database.
def validate_passenger_number
self.errors.add(:base, "exceed maximum entry") if self.passengers.length > 2
end
Call this custom validation in respective model.
  validate :validate_passenger_number, on: :create

There's no built-in validation; at least I haven't come across any. But following is a way to impose this type of validation:
def max_passengers
if self.passengers.count > 2
errors.add_to_base("There should not be more than 2 passengers.")
end
end
And then, you can use this validation to impose a check on the number of passengers.

You can add callback and validate the record.
before_validation :validate_passanger_number
private
def validate_passanger_number
errors.add("Sorry", "you have added maximum passengers.") if self.passengers.count > 2
end

Related

Record being created multiple times in spite of validation (timing issue)

A webhook with lots of processing creates a record that has to go through this validation:
validates_uniqueness_of :pipedrive_deal_id, :allow_blank => true, scope: [:experience_id, :time], unless: -> { pipedrive_deal_id.zero? }
The processing of this webhook generates additional calls of the same webhook. This results in the same record being created 4 times, not respecting the validation shown above.
I "fixed" the issue by putting "sleep 5" on subsequent calls of the webhook, which apparently gives the initial process enough time to "close"? the validation and rejects any subsequent attempts to create it.
Is there a less hacky way to fix this issue?
Here's the code of the method (I have removed some things to make it clearer):
def self.create_or_update_from_pipedrive(params, won_or_update,changed_values=nil)
#this method calls the same webhook
hosting_link = PipedriveService.hosting_platform_setup(params[:id])
if won_or_update == ('won')
Payment.add_deal(params)
else
#this is the offending sleep 5
sleep 5
end
#....internal processing
if booking.update( booking_params)
if booking.tour.present?
if booking.tour.time != booking.time
#this calls the webhook again
booking.add_to_tour
end
end
end
if params[Rails.configuration.pipedrive_hash[:trip_details]].include? '40'
if booking.update(booking_params)
if booking.tour.present?
if booking.tour.time != booking.time
#this calls the webhook again
booking.add_to_tour
end
end
end
#this calls the webhook again
PipedriveService.set_post_session_activities(params, booking_time) if won_or_update == 'won'
end
Thanks
You need a unique index in the database. It is even mentioned in the official rails guides.
https://guides.rubyonrails.org/active_record_validations.html#uniqueness
This helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint in the database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create a unique index on that column in your database.
In case anyone stumbles upon my issue, the solution is (as suggested by the other answers) a unique index in the DB. As for the "unless attribute is zero" part, here is the whole migration command to add this as well:
add_index :bookings, [:pipedrive_deal_id, :time,:experience_id], unique: true, where: 'pipedrive_deal_id > 0'

Having two different sort of validate, from user or app itself in rails

My program is trying to create some groups automatically, with a prefix of 'automated_group', it won't show up when loading all groups, can't be edited, and some more stuff. But I have to limit users from doing it.
But if I make a validate function, it won't let my app do it and group.save returns false. Even when updating other attributes, it won't let me save it, cause the name won't validate.
Is there any other way? Sometimes use validation, or maybe check who's changing the value?
Thanks in advance
You can use a permission system like cancan (https://github.com/ryanb/cancan). Then you can define someting like this:
can :manage, Group, automated_group: false
I've found the half of the answer in skip certain validation method in Model
attr_accessor :automated_creation
validate :check_automated_name, unless: :automated_creation
def check_automated_name
#...
end
and in my controller:
def get_automated_group
name = "automated_group_#{product.id}"
group = Group.find(name: name).first
group Group.new(automated_creation: true) unless group.blank?
returrn group
end
When updating:
I'll check in check_automated_name function that it any change on name has happened with:
group.name_changed?
so any thing else can be updated except 'name', which the only way of creation is from rails itself.

Rails, warnings on Validations

hi all what i am trying to do is create a "soft" validation in other words instead of my failing the validation and not saving the data to the DB, id like to have the validation give a warning to the user and allow the user to save faulty data if they so choose. but the validator will give them a warning before.
i want to do somehting like the following:
class MyModel < ActiveRecord::Base
warnings do
validate :warnings_validation
end
def warnings_validation
warnings.add(:name_of_element, "warning message") unless x == x
end
end
my model uses alot of inheritance and so gems like validations_scope dont work any ideas what i can do/use ?
I believe you could inspire yourself from the ActiveModel::Error example to implement the warning feature.
Explanation :
If you look at how the data is validated in Active Record, simply take a look at the valid? method :
def valid?(context = nil)
context ||= default_validation_context
output = super(context)
errors.empty? && output
end
The context and the output is not important, what matter is that the valid? method check if the errors instance variable is empty or not.
In other words, in the previous link I gave you, simply renaming the errors instance variable into warnings should do the trick. You'd have to create a custom validator using rails built-in and then simply call warnings.add(:name, "error") when needed. It should save the records while populating the warnings variable.
see this - custom validation
try this
validate :warnings_validation
def warnings_validation
self.name_of_element.blank?
errors.add(:name_of_element, "warning message") unless x == x
end
end

Rails validation that one value does not equal another

Is there a way to validate that one text_field does not equal another before saving record? I have two text_fields with integers in them and they cannot be identical for record to be valid.
You can add a custom validation:
class Something
validate :fields_a_and_b_are_different
def fields_a_and_b_are_different
if self.a == self.b
errors.add(:a, 'must be different to b')
errors.add(:b, 'must be different to a')
end
end
That will be called every time your object is validated (either explicitly or when you save with validation) and will add an error to both of the fields. You might want an error on both fields to render them differently in the form.
Otherwise you could just add a base error:
errors.add(:base, 'a must be different to b')
In your model:
validate :text_fields_are_not_equal
def text_fields_are_not_equal
self.errors.add(:base, 'Text_field1 and text_field2 cannot be equal.') if self.text_field1 == self.text_field2
end
more fun:
validates :a, exclusion: { in: ->(thing) { [thing.b] } }
Though of course this isn't terribly readable, but it's elegant - we're leveraging the exclusion validation with a proc to prevent the values from being equal. A more verbose approach might be preferred by some, but I'm a fan of brevity - code that doesn't exist can't have bugs. Plus, this will get its error message the default rails location, which may be convenient for i18n purposes.
(better?)

Soft db error handling for duplicate entries? -- Rails 3.1 newbie

If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end

Resources