1

在我的应用程序中,我有一个与 status_updates 模型有很多关系的用户模型。

目前,我的数据库只保存了 1 个用户和零个 status_updates。

奇怪的是,当我在 consol 中搜索 status_updates 或这些对象之间的关系时,确认数据库中的 status_updates 为 nil。

StatusUpdate.count
(0.2ms)  SELECT COUNT(*) FROM "status_updates" 
=> 0 

StatusUpdate.any?
(0.1ms)  SELECT COUNT(*) FROM "status_updates" 
=> false 



user.status_updates.count
(0.2ms)  SELECT COUNT(*) FROM "status_updates" WHERE "status_updates"."user_id" = 1
=> 0 

user.status_updates.any?
(0.2ms)  SELECT COUNT(*) FROM "status_updates" WHERE "status_updates"."user_id" = 1
=> false 

所以,这对我来说很清楚。

但是,当我在我的应用程序中编写以下内容时,会返回一个 status_update 对象!

def end_date             
 if self.status_updates.any? == false
  return self.status_updates.any?
 elsif self.status_updates.any? == true
  return self.status_updates.first
 end
end  

这是我认为的电话

current_user.end_date

这就是返回给视图的内容:

#<StatusUpdate:0x007fa99765d6f8>

如果我将视图中的调用更改为:

current_user.status_updates.first

=> <StatusUpdate:0x007fa99740b5f8>

但是,如果我这样称呼:

current_user.status_updates.count

=> 0

current_user.status_updates.any?

=> true

在我只使用 current_user.status_updates 的视图中,它返回

[#<StatusUpdate id: nil, created_at: nil, updated_at: nil, user_id: 1, current_weight: 0.0, current_bf_pct: 0.0, current_lbm: 0.0, current_fat_weight: 0.0, change_in_weight: nil, change_in_bf_pct: nil, change_in_lbm: nil, change_in_fat_weight: nil, total_weight_change: nil, total_bf_pct_change: nil, total_lbm_change: nil, total_fat_change: nil>]

这里发生了什么?!


用户模型关系

has_many :status_updates, 依赖: :destroy

状态更新模型关系

属于_to:用户


状态更新模型

class StatusUpdate < ActiveRecord::Base
 belongs_to :user

 after_initialize :default_values 
 before_save :sanitize 

 attr_accessible :current_weight,
               :current_bf_pct,
               :current_lbm,
               :current_fat_weight,
               :change_in_weight,
               :change_in_bf_pct,
               :change_in_lbm,
               :change_in_fat_weight,
               :total_weight_change,
               :total_bf_pct_change,
               :total_lbm_change,
               :total_fat_change,
               :created_at

 validates :user_id,            presence: true
 validates :current_bf_pct,     presence: true, numericality: true, length: { minimum: 2, maximum:5 }  
 validates :current_weight,     presence: true, numericality: true, length: { minimum: 2, maximum:5 } 
 validates :current_lbm,        presence: true
 validates :current_fat_weight, presence: true                   


 def sanitize     
 if self.current_bf_pct >= 0.5
   self.current_bf_pct /= 100
    if self.current_bf_pct <= 0.04
      self.current_fb_pct *= 100
    end 
 end
 self.current_fat_weight = self.current_weight * self.current_bf_pct
 self.current_lbm = self.current_weight - self.current_fat_weight
 end  

 def default_values
 if self.created_at == nil 
  self.current_bf_pct       = 0.20 
  self.current_weight       = 0 
  self.current_lbm          = 0 
  self.current_fat_weight   = 0 
  self.change_in_weight     = 0 
  self.change_in_bf_pct     = 0 
  self.change_in_lbm        = 0 
  self.change_in_fat_weight = 0 
  self.total_weight_change  = 0 
  self.total_bf_pct_change  = 0 
  self.total_lbm_change     = 0 
  self.total_fat_change     = 0 
  end
 end

  def previous_status_update
   previous_status_update = user.status_updates.where( "created_at < ? ", self.created_at ).first   
 if previous_status_update == nil
   return self
 else
   previous_status_update
 end
end 


 default_scope order: 'status_updates.created_at DESC'

end

用户模型:

    class User < ActiveRecord::Base
      # Include default devise modules. Others available are:
      # :token_authenticatable, :confirmable,
      # :lockable, :timeoutable and :omniauthable
      devise :database_authenticatable, :registerable,
            :recoverable, :rememberable, :trackable, :validatable

      before_create :sanitize

      has_many :status_updates, dependent: :destroy
      has_many :meals, dependent: :destroy
      has_many :custom_foods, dependent: :destroy
      has_many :meal_foods, through: :meals
      # after_initialize :default_values
      attr_accessor :user_password, :user_password_confirmation, :current_password
      attr_accessible :email,
                      :password,
                      :password_confirmation,
                      :current_password,
                      :goal,
                      :measurement,
                      :bmr_formula,
                      :fat_factor,
                      :protein_factor,
                      :remember_me,
                      :deficit_amnt,
                      :target_bf_pct,
                      :activity_factor,
                      :current_password

      validates :email,                 presence: true
      validates :target_bf_pct,         presence: true, on: :update, length: { minimum: 3, maximum: 4 }
      validates :activity_factor,       presence: true, on: :update
      validates :deficit_amnt,          presence: true, on: :update
      validates :fat_factor,            presence: true, on: :update
      validates :protein_factor,        presence: true, on: :update

      def new?
        self.created_at <= 1.minutes.ago.to_date ? true : false
      end

      def sanitize
        #inputs
        self.activity_factor       = 1.3
        self.deficit_amnt          = 1
        self.target_bf_pct         = 10 
        self.fat_factor            = 0.45
        self.protein_factor        = 1
      end


      def end_date             
        if self.status_updates.any? == false
          #Time.now
          self.status_updates.any?
        elsif self.status_updates.any? == true
          #(self.start_date + self.weeks_to_goal.to_i.weeks).strftime("%m/%d/%Y")
          self.status_updates
        end
      end

      def start_date           
        if self.status_updates.any? == true
          self.status_updates.first.created_at
        end
      end

      def daily_caloric_deficit 
        self.tdee.to_d - self.daily_intake.to_d
      end

      def current_fat_weight   
        BigDecimal(self.latest_status_update.current_fat_weight, 4)
      end

      def current_lbm          
        BigDecimal(self.latest_status_update.current_lbm, 4)
      end

      def current_bf_pct       
        BigDecimal(self.latest_status_update.current_bf_pct * 100, 4)
      end

      def current_weight       
        BigDecimal(self.latest_status_update.current_weight, 4)
      end

      def total_weight         
        self.latest_status_update.current_weight
      end

    #  def lbm                  
    #    self.latest_status_updates.current_lbm
    #  end

      def recent_weight_change 
        BigDecimal(self.latest_status_update.current_weight - self.latest_status_update.previous_status_update.current_weight, 2) 
      end

      def recent_lbm_change   
        BigDecimal(self.latest_status_update.current_lbm - self.latest_status_update.previous_status_update.current_lbm, 2)
      end

      def recent_fat_change
        BigDecimal(self.latest_status_update.current_fat_weight - self.latest_status_update.previous_status_update.current_fat_weight, 3)
      end

      def total_lbm_change
        BigDecimal(self.latest_status_update.current_lbm - self.oldest_status_update.current_lbm, 3)
      end

      def total_fat_change 
        BigDecimal(self.latest_status_update.current_fat_weight - self.oldest_status_update.current_fat_weight, 3)
      end

      def total_weight_change
        BigDecimal(self.latest_status_update.current_weight - self.oldest_status_update.current_weight, 3)
      end

      def last_date
        self.status_updates.last.created_at.strftime("%m/%d/%Y") 
      end

      def beginning_date
        self.status_updates.first.created_at.strftime("%m/%d/%Y") 
      end

      def latest_status_update
        self.status_updates.first  
      end

      def oldest_status_update
        self.status_updates.last
      end

      def bmr
        cur_lbm = self.current_lbm
        cur_lbm *= 0.45
        '%.2f' % (370 + (21.6 * cur_lbm.to_d))
      end

      def target_weight
        tar_bf_pct = self.target_bf_pct /= 100
        '%.2f' %  ((self.total_weight * tar_bf_pct)+ self.current_lbm)
      end 

      def fat_to_burn
        '%.2f' % (self.total_weight.to_d - self.target_weight.to_d)
      end

      def tdee
        '%.2f' % (self.bmr.to_d * self.activity_factor.to_d)
      end

      def deficit_pct
        daily_cal_def = ((self.deficit_amnt.to_f * 3500)/7)
        (daily_cal_def.to_d/self.tdee.to_d)
      end

      def daily_calorie_burn
        '%.2f' % (self.tdee.to_d * self.deficit_pct.to_d)  
      end

      def weekly_calorie_burn_rate
        '%.2f' % (self.daily_calorie_burn.to_d*7) 
      end

      def weeks_to_goal
        '%.2f' %  (self.fat_to_burn.to_d*3500/self.weekly_calorie_burn_rate.to_d) 
      end                  

      def daily_intake
        '%.2f' % (self.tdee.to_d - self.daily_calorie_burn.to_d)
      end                       

      def total_grams_of(macro)
        self.meal_foods.map(&macro).inject(:+)
      end 

      def pct_fat_satisfied
        #how much of a macro is needed?
        fat_needed = self.fat_factor * self.current_lbm
        #how much is in the meal?
        fat_provided = self.total_grams_of(:fat)
        #percent needed
        pct_fulfilled = fat_provided.to_f/fat_needed.to_f
        BigDecimal(pct_fulfilled, 2)*100
      end 

      def pct_protein_satisfied
        #how much protien is needed?
        protein_needed = self.protein_factor * self.current_lbm
        #how much protien is provided?
        protein_provided = total_grams_of(:protien)
        #pct of protien satisfied?
        pct_fulfilled = protein_provided.to_f/protein_needed.to_f
        BigDecimal(pct_fulfilled, 2)*100
      end    

      def pct_carbs_satisfied
        #how many carbs are needed?
        cals_required = self.tdee.to_f - (self.tdee.to_f * self.deficit_pct.to_f)
        fat_cals = total_grams_of(:fat) * 9
        protien_cals = total_grams_of(:protien) * 4
        #how many carbs are provided?
        cals_provided = fat_cals + protien_cals
        cals_balance = cals_required - cals_provided
        carbs_needed = cals_balance/4
        carbs_provided = total_grams_of(:carbs)
        BigDecimal(carbs_provided / carbs_needed, 2) * 100
      end 

    end

我试过重置我的数据库,同样的问题出现了。


回顾一下:

似乎正在初始化 status_update,并且该对象的初始化版本是视图可用的。所有属性都是初始化的,这就是为什么它没有 id 或创建时间的时间戳......因为它从未被创建。

但是,当我尝试从控制台访问它时,我意识到它给了我“零”和所有可预测的值,因为在控制台中它正在寻找关系中已经创建的对象关系。

所以这里更精确的问题是视图返回对象的初始化版本而不是控制台返回什么?

当应用程序在用户模型中遇到一个方法时,它会抛出一个错误,因为当它调用类似的东西时 status_update 不存在

self.status_updates.first
4

2 回答 2

1

这一行:

#<StatusUpdate id: nil, created_at: nil, updated_at: nil, user_id: 1, current_weight: 0.0, current_bf_pct: 0.0, current_lbm: 0.0, current_fat_weight: 0.0, change_in_weight: nil, change_in_bf_pct: nil, change_in_lbm: nil, change_in_fat_weight: nil, total_weight_change: nil, total_bf_pct_change: nil, total_lbm_change: nil, total_fat_change: nil>]

表示此id: nil模型尚未保存。模型只有在您保存它们后才会获得一个 ID。正如 Frederick 所指出的,它已经user_id: 1表明您可能通过用户创建了它。所以在某个地方你正在创建这个实例而不是保存它。

PS,它被广泛认为是糟糕的红宝石形式来检查布尔值是假还是真。例如

不要这样做:

self.status_updates.any? == false

self.status_updates.any?

无论如何,这是对还是错,然后只需使用ifandunless作为您的检查。更好的是,假设 self ,所以在你的情况下,你可以这样做:

status_updates.any?

PPS active_recordnew?为这个用例定义了一个方法。尚未保存的记录是new,因此new?将返回 true。我强烈建议您不要覆盖此方法或 active_record 定义的任何其他方法。

于 2013-05-29T20:30:09.960 回答
1

你可能在做

@status_update = user.status_updates.build

在您的控制器中 - 这是很常见的地方,因此您可以在页面上放置一个表单,允许用户提交新的状态更新。

该对象未保存,因此

user.status_updates.count

保证命中数据库(并运行select count(*) ...)不计算在内。但是,它位于该用户的状态更新关联的内存缓存中,因此user.status_updates.any?返回 true。

您可能还注意到,将谓词方法的返回值与trueor进行比较是非常不习惯的false。它读起来很有趣,并且像这样的方法可能会返回一个具有等效真实性的值,而不是 true 或 false(例如 nil 而不是 false)。

于 2013-05-29T15:47:11.230 回答