I have a two models:
Polymorphic
class Custo < ActiveRecord::Base
belongs_to :custavel, polymorphic: true
accepts_nested_attributes_for :custavel, allow_destroy: true
validates_presence_of :numero, :serie
validates_uniqueness_of :numero, scope: :serie
end
Another class
class Pagamento < ActiveRecord::Base
has_one :custo, as: :custavel
end
In general numero and serie are filled by user input, but only when the polymorphic is a Pagamento i need to fill the column numero with custavel_id.
Then, i tried it:
before_save :set_numero
def set_numero
return unless custavel_type == 'Pagamento'
write_attribute(:numero, custavel_id)
end
But it fails on validation because custavel_id doesn't exists yet. How to do it?
Modify the callback method
before_save :set_numero
def set_numero
write_attribute(:numero, custavel_id) if custavel_type == 'Pagamento'
end
Related
I have this problem, i tried to get all Beauty Salons when the service has a promotion, but service has a custom method and return true or false, this is the structure of my code
class BeautySalon < ActiveRecord::Base
has_many :services
end
class Service < ActiveRecord::Base
belongs_to :beauty_salon
has_many :service_promotions
has_many :promotions, through: :service_promotions
def has_promotion?
## consult another tables and return true or false if found a promotion
end
end
iam tried to make the query like this
BeautySalon.all.includes(:services).select('services.*').select{|service| service.has_promotion?}
but rails return this error
NoMethodError (undefined method `has_promotion?' for #BeautySalon:0x0055a1119d1f40)
Any advice for this?
UPDATE
the method has_promotion do this
def has_promotion?
if promotions.exists?
if get_promotions(Date.today).exists?
return true
else
return false
end
end
return false
end
def get_promotions(date)
if promotions.exists?
promotions.where('start_date <= ? and end_date >= ?',date,date)
end
end
and another tables are there
class Promotion < ActiveRecord::Base
validates :discount, presence: true
validates :start_date, presence: true
validates :end_date, presence: true
has_many :service_promotions
has_many :services, through: :service_promotions
end
class ServicePromotion < ActiveRecord::Base
validates :service_id, presence:true
validates :promotion_id, presence:true
belongs_to :service
belongs_to :promotion
end
thanks for all the advises
BeautySalon.joins(:services).select { |beauty_salon| beauty_salon.services.any?(&:has_promotion?) }
This is an option. You are now calling has_promotion? on the wrong object (e.g. beauty salon instead of service) and the error is raised because the instance method is defined in Service.rb.
It would be better in my opinion to add a database column has_promotion (boolean) to your services table.
BeautySalon.joins(:services).where(has_promotion: true)
If has_promotion? simply retrieves data from another associated model (other_association, with db boolean column promotion) you can also do something like this:
BeautySalon.joins(services: other_association).where(other_association: { promotion: true })
Update:
BeautySalon.joins(services: :promotions).where("promotions.start_date <= :date AND promotions.end_date >= :date", date: Date.current )
This will return all beauty salons with running service promotions (today).
I want to change has_many association behaviour
considering this basic data model
class Skill < ActiveRecord::Base
has_many :users, through: :skills_users
has_many :skills_users
end
class User < ActiveRecord::Base
has_many :skills, through: :skills_users, validate: true
has_many :skills_users
end
class SkillsUser < ActiveRecord::Base
belongs_to :user
belongs_to :skill
validates :user, :skill, presence: true
end
For adding a new skill we can easily do that :
john = User.create(name: 'John Doe')
tidy = Skill.create(name: 'Tidy')
john.skills << tidy
but if you do this twice we obtain a duplicate skill for this user
An possibility to prevent that is to check before adding
john.skills << tidy unless john.skills.include?(tidy)
But this is quite mean...
We can as well change ActiveRecord::Associations::CollectionProxy#<< behaviour like
module InvalidModelIgnoredSilently
def <<(*records)
super(records.to_a.keep_if { |r| !!include?(r) })
end
end
ActiveRecord::Associations::CollectionProxy.send :prepend, InvalidModelIgnoredSilently
to force CollectionProxy to ignore transparently adding duplicate records.
But I'm not happy with that.
We can add a validation on extra validation on SkillsUser
class SkillsUser < ActiveRecord::Base
belongs_to :user
belongs_to :skill
validates :user, :skill, presence: true
validates :user, uniqueness: { scope: :skill }
end
but in this case adding twice will raise up ActiveRecord::RecordInvalid and again we have to check before adding
or make a uglier hack on CollectionProxy
module InvalidModelIgnoredSilently
def <<(*records)
super(valid_records(records))
end
private
def valid_records(records)
records.with_object([]).each do |record, _valid_records|
begin
proxy_association.dup.concat(record)
_valid_records << record
rescue ActiveRecord::RecordInvalid
end
end
end
end
ActiveRecord::Associations::CollectionProxy.send :prepend, InvalidModelIgnoredSilently
But I'm still not happy with that.
To me the ideal and maybe missing methods on CollectionProxy are :
john.skills.push(tidy)
=> false
and
john.skills.push!(tidy)
=> ActiveRecord::RecordInvalid
Any idea how I can do that nicely?
-- EDIT --
A way I found to avoid throwing Exception is throwing an Exception!
class User < ActiveRecord::Base
has_many :skills, through: :skills_users, before_add: :check_presence
has_many :skills_users
private
def check_presence(skill)
raise ActiveRecord::Rollback if skills.include?(skill)
end
end
Isn't based on validations, neither a generic solution, but can help...
Perhaps i'm not understanding the problem but here is what I'd do:
Add a constraint on the DB level to make sure the data is clean, no matter how things are implemented
Make sure that skill is not added multiple times (on the client)
Can you show me the migration that created your SkillsUser table.
the better if you show me the indexes of SkillsUser table that you have.
i usually use has_and_belongs_to_many instead of has_many - through.
try to add this migration
$ rails g migration add_id_to_skills_users id:primary_key
# change the has_many - through TO has_and_belongs_to_many
no need for validations if you have double index "skills_users".
hope it helps you.
I'm new to Rails and ActiveRecord and need some help. Basically, I have 4 models: User, Property, PropertyAccount, and AccountInvitation. Users and Properties have a many to many relationship via PropertyAccounts. AccountInvitations have a user's email and a property_id.
What I want to happen is that after a user registers on my app, his user account is automatically associated with some pre-created Properties. What I don't know how to do is write the query to get the Property objects from the AccountInvitations and save them to the User object. Please see def assign_properties for my pseudo code. Any help is welcome, thanks so much!
class User < ActiveRecord::Base
has_many :property_accounts
has_many :properties, through: :property_accounts
after_create :assign_properties
# Check to see if user has any pre-assigned properties, and if so assign them
def assign_properties
account_invitations = AccountInvitations.where(email: self.email)
if account_invitations.any?
account_invitations.each do |i|
properties += Property.find(i.property_id)
end
self.properties = properties
self.save
end
end
end
class AccountInvitation < ActiveRecord::Base
belongs_to :property
validates :property_id, presence: true
validates :email, uniqueness: {scope: :property_id}
end
class Property < ActiveRecord::Base
has_many :account_invitations
has_many :property_accounts
has_many :users, through: :property_accounts
end
class PropertyAccount < ActiveRecord::Base
belongs_to :property
belongs_to :user
end
Thanks to #wangthony , I looked at the includes method on http://apidock.com/rails/ActiveRecord/QueryMethods/includes and tweaked one of their examples in order to get this to work. Here's the solution:
def assign_property
self.properties = Property.includes(:account_invitations).where('account_invitations.email = ?', self.email).references(:account_invitations)
self.save
end
I believe you can do this:
user.properties = Property.includes(:account_invitations).where(email: user.email)
user.save
I am really new in Ruby and I am on the last step to finish my project, when I'm trying to add appointment I have to change if doctor works in that time. I don't know how to do this :(
It is how my db works:
In appointment I have data_wizyty (visit_date), doctor_id and godzina_wizyty(visit_time) - it is in my adding form.
In schedules I have:
dzien_tygodnia(day_of_the_week), poczatek_pracy(start_working), koniec_pracy(end_working) and doctors_workplace_id
In doctors_workplace:
doctor_id, schedule_id, clinic_id
I want to check if doctor is available in any of the clinic in choosen date and time :)
Please help me with this :)
I have already validated if date and time is unique with:
class Appointment < ActiveRecord::Base
validates :doctor_id, uniqueness: { scope: [:data_wizyty, :godzina_wizyty], message: 'Ten termin jest juz zajety!' }
end
I need to check if it is unique and if doctor works.
Appointment:
class Appointment < ActiveRecord::Base
validates :doctor_id, uniqueness: { scope: [:data_wizyty, :godzina_wizyty], message: 'Ten termin jest juz zajety!' }
after_initialize :aInit
after_save :aSave
belongs_to :patient
belongs_to :doctor
belongs_to :schedule
belongs_to :refferal
belongs_to :clinic
has_many :employees
include MultiStepModel
def self.total_steps
3
end
def aInit
#wymaga_Potwierdzenia = true
end
def aSave
if self.refferal_id == nil
#potwierdzona = false
else
#potwierdzona = true
end
if self.wymaga_Potwierdzenia == false
#potwierdzona = true
end
end
end
Schedule:
class Schedule < ActiveRecord::Base
has_many :appointments
belongs_to :clinic
belongs_to :doctors_workplace
def full_schedule
"#{dzien_tygodnia} : #{poczatek_pracy} - #{koniec_pracy}"
end
end
Doctors_workplace:
class DoctorsWorkplace < ActiveRecord::Base
has_many :schedules
belongs_to :doctor
belongs_to :clinic_surgery
end
Now I have something like this :
def check_doctor_available
if Schedule.where(doctor: doctor, dzien_tygodnia: data_wizyty.wday)
.where('poczatek_pracy < ? and koniec_pracy > ?', godzina_wizyty, godzina_wizyty).empty?
self.errors.add(:doctor, message: 'nie pracuje w tym terminie!')
end
It's what I have now:
def check_doctor_available
if DoctorsWorkplace.where(doctor_id: doctor_id) and
Schedule.where(doctors_workplace_id: ????, dzien_tygodnia: data_wizyty.wday)
.where('poczatek_pracy < ? and koniec_pracy > ?', godzina_wizyty, godzina_wizyty).empty?
self.errors.add(:doctor, message: 'nie pracuje w tym terminie!')
end
You can use a custom validation. Create a private method in appointment that checks if the doctor is available at the given date/time.
validate :check_doctor_available
private
def check_doctor_available
#your implementation
end
Take a look at this if you have any doubts what to write in your custom validation method.
For example there are some models
class Model_1 < ActiveRecord::Base
has_many :images, :as => :imageable
end
class Model_2 < ActiveRecord::Base
# doesn't have has_many association
end
...
class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
How can I check that model has has_many association? Something like this
class ActiveRecord::Base
def self.has_many_association_exists?(:association)
...
end
end
And it can be used so
Model_1.has_many_association_exists?(:images) # true
Model_2.has_many_association_exists?(:images) # false
Thanks in advance
What about reflect_on_association?
Model_1.reflect_on_association(:images)
Or reflect_on_all_associations:
associations = Model_1.reflect_on_all_associations(:has_many)
associations.any? { |a| a.name == :images }
I found the following to be the simple way to achieve the desired result:
ModelName.method_defined?(:method_name)
Example:
Model_1.method_defined?(:images) # true
Model_2.method_defined?(:images) # false
Reference: https://stackoverflow.com/a/18066069/936494
You could probably use respond_to?
class ActiveRecord::Base
def self.has_many_association_exists?(related)
self.class.associations.respond_to?(related)
end
end
You could just have a method that tries to access a Model_1 object images inside an exception block like (roughly) :
begin
model1_obj.images
rescue
puts 'No association between model_1 and images'
end
Inside rescue, you can just return false if you like.