2

我对如何制作这个范围或方法感到困惑。我有以下关联:

楷模

class User
  has_many :prices
  has_many :products, :through => :prices
  has_many :subscriptions, :foreign_key => :subscriber_id
end

class Product
  has_many :prices
  has_many :users, :through => :prices
end

class Price
  # Table columns => :product_id, :cost, :user_id
  belongs_to :user
  belongs_to :product
  belongs_to :store
  has_many :subscriptions, :as => :subscribable
end

class Subscription
  # Table columns => :product_id, :cost, :subscriber_id, :subscribable_id
  # :subscribable_type
  belongs_to :subscriber, :class_name => "User"
  belongs_to :subscribable, :polymorphic => true
  validates_uniqueness_of :subscribable_id, :scope => 
                        [ :subscriber_id, :subscribable_type]
end

所以方法应该是这样的:

class Price

def self.lower_price
  if self.product_id == self.subscription.product_id
     if self.cost < self.subscription.cost
     end
  end
end

end

该方法的目的是仅显示UserProducts与 相同Product的价格较低的价格Subscription,同时将自身与订阅price字段进行比较以查看其是否较低。

我这样做对吗?需要修复什么?


编辑

class Price < ActiveRecord::Base
  scope :for_product, lambda { |product_id| where( :product_id => product_id) }
  scope :cheaper, lambda { |cost| where(["prices.cost < :cost", { :cost => cost } ] ) }
end

class Subscription < ActiveRecord::Base

  def cheaper_prices
    Price.for_product(product_id).cheaper(cost)
  end
end

PrivatePagesController:

def watch
 @prices = Price.cheaper_prices.paginate(:page => params[:page], :per_page => 20).order('purchase_date DESC')
end

This gives me the error:

NoMethodError in PrivatePagesController#watch

undefined method `cheaper_prices' for #<Class:0x6f99210>
4

3 回答 3

1

首先,您应该将 UserProduct 表命名为更有意义。除了作为链接表之外,我无法说出它的语义是什么。

订阅产品或用户产品也有一些混淆。我看到了多态关联,但我怀疑存在一些混淆。

我注意到 Subscription 有一个 product_id,所以这告诉我 Subscription 属于 Product,而不是属于可订阅的。

所以,首先你可能需要清理你的设计。

但是,假设我可以相信您的条件是您想要的,那么您在 SQL 中想要的是

SELECT cheaper.*
FROM user_products
  INNER JOIN subscriptions ON subscribable_type = 'UserProduct'
                           AND subscriptions.subscribable_id = user_products.id
  INNER JOIN user_products cheaper ON cheaper.product_id = subscriptions.product_id
WHERE cheaper.price < user_products.price

这将为您提供一份您会发现的所有更便宜价格的报告。对于给定 user_products 记录的所有更便宜的价格,您需要包含一个条件,即它适用于给定的 id。

接下来,为了在 ActiveRecord 中进行这项工作,我们希望选择在该类的表上,所以让我们将 SQL 转换为

SELECT user_products.*
FROM user_products
  INNER JOIN subscriptions ON user_products.product_id = subscriptions.product_id
  INNER JOIN user_products target ON subscribable_type = 'UserProduct'
                           AND subscriptions.subscribable_id = target.id
WHERE user_products.price < target.price
  AND target.id = ?

现在我们准备进行 ActiveRecord 调用。

我不知道 ActiveRecord 是否可以从关联中形成联接。我知道 Rails 2.3 API 需要一个字符串。因此,范围将是:

 class UserProduct
   ...

   #defines UserProduct.cheaper(user_product_id)
   scope :cheaper, lambda do |user_product_id|
     join(%Q{INNER JOIN subscriptions
             ON user_products.product_id = subscriptions.product_id
             INNER JOIN user_products target
             ON subscribable_type = 'UserProduct'
             AND subscriptions.subscribable_id = target.id}).
    where("cheaper.price < user_products.price").
    where(["target.id = :target_id", { :target_id => user_product_id } ] )
  end

  #defines user_product.cheaper
  def cheaper
    UserProduct.cheaper(id)
  end
...
于 2012-04-11T22:06:51.800 回答
1

我认为您正在创建一个网站,用户可以在其中输入他们找到的价格并订阅以找到更便宜的价格。我将 UserProduct 实体重命名为 Price。

订阅者是订阅产品还是订阅价格是模棱两可的。如果你清除它,它可能会简化多态关联。假设他们以给定的价格订阅了产品。然后你想要以下内容:

class Price
  # Table columns => :product_id, :price, :user_id
  belongs_to :finder, :class_name => "User"
  belongs_to :product
  belongs_to :store

  scope for_product, lambda { |product_id| where(:product_id => product_id)
  scope cheaper, lambda { |price| where([ "prices.price < :price", {:price => price} ] }
end

class Subscription
  # Table columns => :product_id, :price, :subscriber_id
  belongs_to :subscriber, :class_name => "User"
  belongs_to :product
  validates_uniqueness_of :subscribable_id, :scope => 
                        [ :subscriber_id, :subscribable_type]

  def cheaper
    Price.for_product(product_id).cheaper(price)
  end
end
于 2012-04-12T10:37:58.630 回答
0

如果您可以假设您拥有的每个产品都有订阅,那么这应该适合您

  scope :lower_priced_user_products, self.joins("join subscriptions on subscriptions.product_id = user_products.product_id").where("user_products.product_id < subscriptions.product_id")

这将返回一个只读记录。如果您想要读/写访问权限,那么您应该使用 UserProduct.find(rec.id) 重新加载记录。

让我知道事情的后续。

于 2012-04-11T22:04:48.630 回答