Ruby on rails RubyonRails:检查商店拥有的产品数量

Ruby on rails RubyonRails:检查商店拥有的产品数量,ruby-on-rails,ruby,Ruby On Rails,Ruby,为了更好地理解Rails,我正在忙于一个测试/练习项目 在我的例子中,我有三个模型:商店、用户和产品 商店可以有三种类型:基本型、中型、大型。基本型最多可以有10个产品,中型50个,大型100个 我试图验证这类数据、商店类型,并在创建新产品时检查它拥有多少产品 到目前为止,我在shop.rb中提出了这段代码,但它不起作用: def lol account = Shop.find_by_sql "SELECT account FROM shops WHERE user_id = 4

为了更好地理解Rails,我正在忙于一个测试/练习项目

在我的例子中,我有三个模型:商店、用户和产品

商店可以有三种类型:基本型、中型、大型。基本型最多可以有10个产品,中型50个,大型100个

我试图验证这类数据、商店类型,并在创建新产品时检查它拥有多少产品

到目前为止,我在shop.rb中提出了这段代码,但它不起作用:

  def lol
      account = Shop.find_by_sql "SELECT account FROM shops WHERE user_id = 4 LIMIT 1"
    products = Product.count_by_sql "SELECT COUNT(*) FROM products WHERE shop_id = 13"
    if account = 1 && products >= 10
        raise "message"
    elsif   account = 2 && products >= 50
        raise "message"
    else account = 3 && products >= 100
        raise "message"
    end
end
我甚至不知道我的解决方案背后的逻辑是否正确。也许我应该使用

has_many

它的“大小”方法是什么?我不知道。:)

应该对你有所帮助。

至少将
account=1
更改为
account==1
account=2
account=3
也是如此

除此之外,我建议您查看以了解Rails的使用

话虽如此,我建议如下:

class Shop < ActiveRecord::Base
  has_many :products
  validates :products_within_limit

  # Instead of the 'account' column, you could make a 'max_size' column.
  # Then you can simply do:
  def products_within_limit
    if products.size > max_size
      errors.add_to_base("Shop cannot own more products than its limit")
    end
  end

  def is_basic?
    products.size >= 10 && products.size < 50 
  end

  def is_medium?
    products.size >= 50 && products.size < 100
  end

  def is_big?
    products.size >= 100
  end

  def shop_size
    if self.is_basic?
      'basic'
    elsif self.is_medium?
      'medium'
    elsif self.is_big?
      'big'
    end
  end
end

这里有一种可能的实现方法。这是我自己的风格,让Ruby模仿抽象类(Shop)的行为。YMMV

编辑:注意,我将OP示例中的“account”变量替换为使用ActiveRecord的继承,后者使用“type”列来执行基本相同的功能,但使用继承来表示不同类型的商店及其各自的产品限制。OP的原始示例可能违反了标准,而STI是解决这一问题的一种方法

编辑:就好像我不够学究,从技术上讲,这并不是一个真正的利斯科夫违规行为,更像是一个错误。它们都是同一主题的变体。你明白了

class Product < ActiveRecord::Base
  belongs_to :shop
end

class Shop < ActiveRecord::Base
  has_many :products  
  belongs_to :user
  validates :products_within_limit

  def products_within_limit
    if products.count > limit
      errors.add_to_base("Shop cannot own more products than its limit")
    end
  end

  def limit
    raise "limit must be overridden by a subclass of Shop."
  end
end

class BasicShop < Shop
  def limit
    10
  end
end

class MediumShop < Shop
  def limit
    50
  end
end

class LargeShop < Shop
  def limit
    100
  end
end

shop = BasicShop.create
10.times {Product.create(:shop => shop)}
shop.reload
shop.valid? # is true
shop.products << Product.new
shop.valid? # is false
类产品限制
错误。将\添加到\基础(“商店不能拥有超过其限制的产品”)
结束
结束
def限制
raise“限制必须被Shop的子类覆盖。”
结束
结束
class BasicShopshop)}
重新装填
商店,有效吗是真的

shop.products不需要这么难:

class Shop < ActiveRecord::Base
  has_many :products

  validate :check_nr_of_products

  def check_nr_of_products
    nr_of_products = products.size
    errors[:base] << "Basic shops can have max 10 products" if account == 1 && nr_of_products > 10
    errors[:base] << "Medium shops can have max 50 products" if account == 2 && nr_of_products > 50
    errors[:base] << "Big shops can have max 100 products" if account == 3 && nr_of_products > 100
  end        
class-Shop
每次保存时都会检查此验证。您不需要检索“帐户类型”,假设它是商店的一个字段。同样,不要编写查询来计算产品的nr,而是使用执行此操作的
size
函数


这是一个简单的解决方案。@Dave_Sims建议的STI解决方案有效且更面向对象。

首先,感谢大家的大力帮助和深入讨论。:)

我从你的答案中提取了一些信息,以便组合出一个我能理解自己的解决方案。似乎在编程方面,我只能理解if-else语句,没有比这更复杂的了(

我所做的是:

class Shop < ActiveRecord::Base

belongs_to :user
has_many :products, :dependent => :destroy
validate :is_account                                                

def is_account
    if account == 1 && products.size < 11
    elsif account == 2 && products.size < 51
    else account == 3 && products.size < 101
    end
end
商店现在是类型1,只有9种产品,但每当我单击“新产品”链接时,我就会被重定向到/products,并显示shops.upgrade.must消息

我不知道,似乎

account 

in shop.rb没有返回正确的值。该列是int(11)类型,因此我猜它只能返回一个数字,但仍然…

再次感谢您的大力支持。我最终从您的解决方案中窃取了一些内容并实现了以下代码:

#in shop.rb
validate :is_account                                                

def is_account
    if account == 1
        limit = 10
    elsif account == 2
        limit = 50
    else account == 3
        limit = 100
    end
    errors.add(:base, "Reached maximum number of items for shop") if account == account && products.size >= limit
end

#in products_controller.rb

def new
if current_user.shop.nil?
    flash[:alert] = I18n.t 'shops.create.must'
    redirect_to :action => :index
elsif current_user.shop.invalid?
    flash[:alert] = I18n.t 'shops.upgrade.must'
    redirect_to :action => :index
else
    @product = Product.new
  end
end
到目前为止似乎还有效。希望我没有犯任何明显的错误


再次感谢!:)

请随意添加带有否决票的评论。你到底不喜欢这里什么?请解释一下反对票。这里的设计基本上是坚固的。OP正在试验rails,这是一个很好的OOP解决方案,引入了STI和自定义验证。我可以在这里看到变化——使用类变量而不是“limit”方法或其他可能性。但是,这个解决方案是完全有效的。总之,没有解释,这些下注是无稽之谈,OP应该考虑我的解决方案或一些变体作为STI+自定义验证的一个很好的例子。p用“type”变量将模型弄乱会引起Liskov的愤怒。无论你想使用“类型”(或在这种情况下,“帐户”)在业务规则上旋转,你都应该考虑多态性(经典意义上的)解决方案。我投了赞成票。对不起,还是-1;-)谢谢船长!有时投票习惯可能会让人困惑。OP给出了一个“账户”变量,以根据所含产品的数量确定特定商店类型的有效性。您给出的不是验证,而是动态确定类型的方法。添加了验证建议,但我个人支持Dave的解决方案。您的验证测试是错误的。我不明白您想做什么,因为您的if块中没有代码。您应该通过执行
错误来添加到
错误中。将\u添加到\u base
错误[:base]
account 
#in shop.rb
validate :is_account                                                

def is_account
    if account == 1
        limit = 10
    elsif account == 2
        limit = 50
    else account == 3
        limit = 100
    end
    errors.add(:base, "Reached maximum number of items for shop") if account == account && products.size >= limit
end

#in products_controller.rb

def new
if current_user.shop.nil?
    flash[:alert] = I18n.t 'shops.create.must'
    redirect_to :action => :index
elsif current_user.shop.invalid?
    flash[:alert] = I18n.t 'shops.upgrade.must'
    redirect_to :action => :index
else
    @product = Product.new
  end
end