如何从Rails中的另一个模型中读取属性

时间:2017-12-13 18:20:52

标签: activerecord ruby-on-rails-5

我仍然认为自己是Rails的新手。我正在Rails应用程序中实现一个SMS功能,以提醒客户即将到来的约会。我的问题是,我的预约模式中有SMS方法,但我的客户端模型是phone属性所在的位置。如何从约会模型中调用我的手机属性。

这是我的预约模式

class Appointment < ApplicationRecord
  enum status: { confirmed: 0, rescheduled: 1, cancelled: 2}
  belongs_to :user
  belongs_to :client
  validates :start_time, presence: true
  validates :end_time, presence: true

  after_create :reminder

  def reminder
    @twilio_number = ENV['TWILIO_NUMBER']
    account_sid = ENV['TWILIO_ACCOUNT_SID']
    @client = Twilio::REST:Client.new account_sid, ENV['TWILIO_AUTH_TOKEN']
    time_str = ((self.start_time).localtime).strftime("%I:%M%p on %b. %d, %Y")
    reminder = "Hi #{client.name}. Just a reminder that you have an appointment coming up at #{time_str}."
    message = @client.api.account(account_sid).messages.create(
      :from => @twilio_number,
      :to => client.phone_number,
      :body => reminder,
    )
  end

我的客户模式

class Client < ApplicationRecord
  has_many :appointments
  has_many :users, through: :appointments

  scope :clients_by, ->(user) { where(user_id: user.id) }
end

根据我当前的关联设置。在提醒变量中我不能只调用

reminder = "Hi #{client.name}.?

:to => client.phone_number

访问phone_number属性?

1 个答案:

答案 0 :(得分:1)

是的,您的假设是正确的,您只需拨打client.<attribute>

但是,请注意可怕的SELECT N + 1问题。所以,假设您做了类似

的事情
Appointment.all.each do {|a| a.reminder }

如果你有50个约会,这将导致51个数据库调用,一个调用加载所有约会,然后一堆调用逐个加载每个客户端。

要避免此问题,您可以使用includeseager_loadspreload,它们比单个查询更有效地加载相关数据。

本文http://blog.scoutapp.com/articles/2017/01/24/activerecord-includes-vs-joins-vs-preload-vs-eager_load-when-and-where详细介绍了这三种方法之间的区别。我引用了TL; DR摘录如下。

  

我粗略地概括了我对这些方法的处理方法,如下所示:      如果我只是过滤,请使用连接。       如果我正在访问关系,请从包含开始。       如果使用两个单独的查询包含很慢,我将使用eager_load强制执行单个查询并比较性能。

     

通过ActiveRecord访问关系时有许多边缘情况。希望这足以在使用连接,包含,预加载和eager_load时阻止一些更基本的性能延迟。

按照那篇文章的建议,我们将我的例子重写为

Appointment.all.includes(:client).each do {|a| a.reminder }