I have a table to enter Vehicle Names, model name is Vehicle
I dont want name to repeat..so i have
validates_uniqueness_of :make, :case_sensitive => false
in my model. Thing is I have used soft delete to delete a record by setting is_deleted flag field to true. This is_deleted column is also present in Vehicle model.
So the problem is if I delete 1 table it just soft-deletes it and when I try to create one vehicle with same name which was soft-deleted then error occures because of validation as the field is not originaly deleted from DB.
Is there a simple way to solve this issue.
I believe this should do the trick:
validates_uniqueness_of :make, :case_sensitive => false, unless: :is_deleted
Conditions
From the sounds of it, you'll want to use a conditional validation, which will look up the name with the constraints of it not having is_deleted? attribute set to true:
#app/models/your_model.rb
Class YourModel < ActiveRecord::Base
validates :make, uniqueness: { scope: :name, message: "Only One Name Allowed Sorry!" }, if: :is_unique?
def is_unique?
return Model.find_by({name: name, is_deleted: false}) #-> will look up any data records which have is_deleted as false & same name
end
end
Related
I want to validate a "House Name" for each "User" .
if i just put
validates :House_name , uniqueness: true
then it will check for all housenames. I want to validate only based on my current user. i.e, one user cannot have multiple house names in same name but other users can have the same house name .
I had a similar problem a while back. There are two solutions depending on how you have it setup.
In your user.rb you can add a custom method like so (assuming User has an array called house_names:
validate :house_name_is_unique
def house_name_is_unique
unless (house_names.length == houses.uniq.length)
errors.add(:house_names, :blank, message: "name taken")
end
end
The above code will check the array of house names, and the uniq method filters out duplicates so if they are the same length, there were no duplicates so do nothing, otherwise complain.
Use scope (untested) You can add the following to your house.rb class
validates :name, uniqueness: { scope: :user_id, message: "no duplicate house name" }
You can read more about scope here: http://guides.rubyonrails.org/active_record_validations.html#uniqueness
Assuming your user model has has_many: houses association, you can try this:
validate :house_uniqueness
def house_uniqueness
errors.add(:base, "House name has been taken") if House.exists?(user: self, name:
self.house_name)
end
Try this:
class User
has_many :houses
end
class House
belongs_to :user
validates_uniqueness_of :name, scope: :user_id
end
API docs
I have a Release model with medium and country columns (among others). There should not be releases that share identical medium/country combinations.
How would I write this as a rails validation?
You can use a uniqueness validation with the scope option.
Also, you should add a unique index to the DB to prevent new records from passing the validations when checked at the same time before being written:
class AddUniqueIndexToReleases < ActiveRecord::Migration
def change
add_index :releases, [:country, :medium], unique: true
end
end
class Release < ActiveRecord::Base
validates :country, uniqueness: { scope: :medium }
end
All the above answers are missing how to validate the uniqueness of multiple attributes in a model. The code below intends to tell how to use multiple attributes in a scope.
validates :country, uniqueness: { scope: [:medium, :another_medium] }
It validates uniqueness of country in all rows with values of medium and another_medium.
Note: Don't forget to add an index on the above column, this insures fast retrieval and adds a DB level validation for unique records.
Update: For adding an index while creating table
t.index [:country, :medium, :another_medium], unique: true
You can pass a :scope parameter to your validator like this:
validates_uniqueness_of :medium, scope: :country
See the documentation for some more examples.
I am using the Rails Admin gem. When I add a new activity type and create it again with the same name, it validates that name is already taken. But whenever I try to edit one it will give you an error: "name can't be blank"
For example, I created Swimming, and I tried to add a new activity type which is swimming/SWIMMING etc. To avoid this I used the before_validation callback, to make the first letter a capital, then check the uniqueness of name.
Yes, it's working but whenever I try to edit the name field it will become blank after I submit it.
NOTE: I also tried to use validates :name, presence: true, :uniqueness => {:case_sensitive => true} only without the before_validation but it didn't work.
Activity Type
class ActivityType < ApplicationRecord
before_destroy :ensure_has_no_activity_type
before_validation :capitalize_first_letter_name
has_many :activities
validates :name, presence: true,:uniqueness => {:case_sensitive => true}, length: { maximum: 20 },format: Utilities::RegexValidations.alphanumeric_underscore
validates :description, presence: false
private
def ensure_has_no_activity_type
if activities.present?
errors.add(:base, 'Cannot delete activity type that has activity')
throw(:abort)
end
end
def capitalize_first_letter_name
# Capitalize the first letter and the rest will be small letter
self.name = self.name.capitalize!
end
end
Question: Why whenever I tried to edit and try to submit it, does the name field become blank? What is the reason for this?
The problem arises from capitalize_first_letter_name. "".capitalize! will return nil. If you change it to "".capitalize that will return blank string as expected.
Moreover, capitalize! will return nil if no changes were made. See https://ruby-doc.org/core-2.2.0/String.html#method-i-capitalize-21.
In our website we will have a search history feature so users can view and retrieve their last x number of searches for the current day.
I would like to check that the user hasn't already entered the same keyword for the current day before creating a new record. These records would be kept in the db for a few days before being removed so if I just validate the uniqueness of the keyword and the user entered that keyword in the past, the record would not be created.
Below is how I have my model and controller setup. Bear with me, I'm still learning about rails and scopes.
MODEL
class UserLog < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :query_type, presence: true
validates :keyword, presence: true
validates :url, presence: true
validates_uniqueness_of :id
scope :user_searches, -> (user = nil) {where(user_id: user).order(created_at: :desc)}
scope :today_only, -> {where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)}
end
I believe I could add these checks in my model that would do what I want.
validates_uniqueness_of :keyword, scope: :keyword, conditions: -> {where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)}
OR THIS?
validates_uniqueness_of :keyword, conditions: -> {where(created_at: Time.now.beginning_of_day..Time.now.end_of_day)}
And the controller
# to save user query in db
if query_valid (other checks in controller)
UserLog.create(user_id: current_user.id, query_type: query_type, keyword: query_value, url: request.fullpath)
end
And to get records to display on user request
#recent_searches = UserLog.user_searches(current_user).today_only.limit(15)
The whole Time.now.beginning_of_day..Time.now.end_of_day sounds overcomplicated to me. How about you store created_on just like created_at, but a Date, not a DateTime. Your uniqueness scope becomes much easier, similarly creation could be:
current_user.logs.where(keyword: query_value, created_on: Date.today).first_or_create(other_fields)
I'm assuming user has_many :logs, for readability. Instead of UserLog.create(user_id: current_user.id, ...
I have a categories model. I want to ensure that a user doesn't add a duplicate category name to his/her list of categories.
Here's my categories model:
class Category < ActiveRecord::Base
belongs_to :user
validates :name, presence: true
validates :user_id, presence: true
before_validation :validate
private
def validate
errors.add(:name, "is already taken") if Category.where("name = '?' AND user_id = ?", self.name, self.user_id).any?
end
end
Here is my RSpec test:
it "is invalid with duplicate name for same user" do
existing_category = Category.first
new_category = Category.new(:name => existing_category.name, :user_id => existing_category.user_id)
expect(new_category).to have(1).errors_on(:name)
end
Should I use before_save or before_validate? Also, I'm unsure how to write this. I guess if a duplicate is detected, I want to add an error for :name. Above is my attempt but doesn't seem to make it pass, is there anything obviously wrong? Also, is this good practise for adding custom validation?
Here is a much simpler way to achieve your goal - you can use scope option of validates_uniqueness_of validator:
validates_uniqueness_of :name, scope: :user_id
Your spec fails because it has an error. It expect new_category has error, but it doesn't run validations on this object. To do that, you just need to add:
new_category.valid?
before expect#... line.