I'm trying to have a record with ends_at column which is suppose to have the created_at value with 5 minutes added to it for instance:
Class Post < ApplicationRecord
after_create :set_end_time
private
def set_end_time
self.ends_at = self.created_at + 5.minutes
end
end
this saves the ends_at column as a null in the database
You fire after_create hook. It gets triggered after Rails calls SQL INSERT command. So you setup ends_at already when object was saved to the database and it will not be saved again. Object with initialized ends_at not going anywhere and then just get cleared from the memory.
Replace your hook with before_create and it should do the trick for you.
You forgot to save the record
def set_end_time
self.ends_at = self.created_at + 5.minutes
save
end
One hard way to do it would be to set a default value when a row is created if the migration
for exemple
change_column_default :posts, :end_time, "current_timestamp + (5 ||' minutes')::interval" if you use postgres for exemple.
But only do that if you are sure that this is not going to change soon.
If it is, then setting a before_create hook with a custom setter is the way to go (Just be sure that your field is of datetime type)
Related
I have a before_create function which i am using to populate a column in the table . For instance
before_create :generate_number
def generate_number
end
I want to check the uniqueness of the column before its populated based on a condition .
example : if the number is created in database 2years ago i dont want the uniqueness validation to be applied on it
how could i write a custom validation for it. And where i should include it?
You can use unless or if to apply logic to a validation:
validate_uniqueness_of :column_name, unless: -> { created_at < 2.years.ago }
This would go in your ActiveRecord model in the same manner as your before_create
class User < ActiveRecord::Base
#...
validate_uniqueness_of :column_name, unless: -> { created_at < 2.years.ago }
end
Since you specified in a comment that you'd like for it to continue generating numbers until one is generated that is not already in the database:
def generate_number
begin
value = generate
end while created_at < 2.years.ago && Table.exists?(column: value)
self.column = value
end
def generate
... # Code to generate number here
end
This continues looping while the created_at column is sooner than 2 years ago and the number generated is in the database. I put the while conditional at the bottom so that generate will run at least once before being checked.
I still don't understand the purpose of the created_at conditional, I can modify that part if you would like to specify how it should work. Otherwise, I'll just consider it an example, and not part of the actual issue.
I have a model which have two columns admin_approved and approval_date. Admin update admin_approved by using activeadmin. I want when admin update this column approval_date also update by current_time.
I cant understand how I do this.Which call_back I use.
#app/models/model.rb
class Model < ActiveRecord::Base
before_update 'self.approval_date = Time.now', if: "admin_approved?"
end
This assumes you have admin_approved (bool) and approval_date (datetime) in your table.
The way it works is to use a string to evaluate whether the admin_approved attribute is "true" before update. If it is, it sets the approval_date to the current time.
Use after_save callback inside your model.
It would be something like this:
after_save do
if admin_approved_changed?
self.approval_date = Time.now
save!
end
end
Or change the condition as you like!
You could set the approval_date before your model instance will be saved. So you save a database write process instead of usage of after_save where you save your instance and in the after_save callback you would save it again.
class MyModel < ActiveRecord::Base
before_save :set_approval_date
# ... your model code ...
private
def set_approval_date
if admin_approved_changed?
self.approval_date = Time.now
end
end
end
May be in your controller:
my_instance = MyModel.find(params[:id])
my_instance.admin_approved = true
my_instance.save
Is there was a way to assign attr_readonly after update?
attr_readonly, on: :update
If not, perhaps a method
#post.update(content: params[:content])
#post.readonly
You could override readonly? in that model like this:
def readonly?
super || created_at != updated_at
end
Rails checks if a record is readonly before it tries to saves an updated record to the database and would raise an ActiveRecord::ReadOnlyRecord exception if the record is marked as readonly. This overridden readonly? method protects a record from being changed twice, by returning always true if the record was changed at least once (indicated by different timestamp on updated_at and created_at).
Furthermore this allows you to check in your view item.readonly? to hide links to the edit page.
you can create a before_update
before_update :forbid_second_update
def forbid_second_update
if created_at != updated_at_was
errors.add :base, "Cannot updated!"
false
end
end
first time update will be successful as created_at and updated_at will be same
second time it will fail
or alternatively if you want to lock some attributes and don't want to fail the update, you can just add for eg.
self.email = self.email_was
this will override the email attribute to its old value
You can add a count into your Model
rails g scaffold sport name
rails g migration add_modified_count_to_sports modified_count:integer
I'm assigning a default value
class AddModifiedCountToSports < ActiveRecord::Migration
def change
add_column :sports, :modified_count, :integer, default: 0
end
end
rake db:migrate
On my Sport model I create a before_update sort of validation
class Sport < ActiveRecord::Base
before_update :validate_update_status
def validate_update_status
unless self.modified_count.eql?(1)
#if the field 'modified_count = 0'
self.modified_count = 1
else
errors.add(:name,'You can only modified your account once')
false
end#end unless
end#def
end#class
You could also implement the same with a State Machine like gem (i.e. assm)
voilĂ !
I'm trying to put a validation on a record. The validation will check that the record can't be created if the ip_address and post_id are the same. This works good.
I am trying to add another condition that will allow this duplication only if it is after a 24 hour period, if so, allow it to save, but all future saves will be disabled again until the 24 period is over.
Here has been my best attempt so far:
validates_uniqueness_of :ip_address, scope: :impressionable_id,
conditions: -> {where('created_at < ?', Time.now - 24.hours)}
So the validation should somehow only check the "latest" record of the group it finds to do the validation to be accurate
Thank you!
It might be easier to just make an explicit validator method for this:
class Foo < ActiveRecord::Base
validate :ip_address_uniqueness, on: :create
# ...
def ip_address_uniqueness
existing = Foo.where(impressionable_id: self.impressionable_id)
.where(ip_address: self.ip_address)
.where(created_at: Time.current.all_day)
errors.add(:ip_address, 'cannot be used again today') if existing
end
end
I have a model Booking with attr_acessor :date and time:
class Booking < ActiveRecord::Base
# ...
before_save :convert_date_and_time
attr_accessor :date, :time
def convert_date_and_time
self.date_and_time = DateTime.parse("#{date.to_s} #{time.to_s}")
end
end
I am trying to define getter methods for date and time:
def date
date_and_time.to_date if self.id.present?
end
def time
date_and_time.to_time if self.id.present?
end
but I think this is not quite the way to do it. I need self.id.present? because when I am trying to create a new record, obviously date_and_time still has no value and the getters will yield errors.
If that is the case, how should the getters look like so that I can handle new records that are not yet saved? Should I leave them like how they are now?
Thanks!
To detect new record you can use new_record?, but in your case you can use try :
date_and_time.try(:to_date)
date_and_time.try(:to_time)