2010-10-12 37 views
3

爲了更好地理解Rails,我正在搞一個測試/練習項目。Ruby on 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 

及其「大小」的方法?我不知道。 :)

回答

6

至少改變account = 1account == 1account = 2account = 3也是如此。

除此之外,我會建議你在看Rails Guides來感受一下使用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 

這允許你做:

# Get shop with id = 1 
shop = Shop.find(1) 

# Suppose shop '1' has 18 products: 
shop.is_big? # output false 
shop.is_medium? # output false 
shop.is_basic? # output true 
shop.shop_size # output 'basic' 
+0

OP給出了一個「賬戶」變量來確定特定商店類型與所含產品數量的有效性。你給出的不是驗證,而是一種動態確定類型的方法。 – 2010-10-12 17:46:37

+0

你說得對,我應該更好地閱讀OP。 – Mischa 2010-10-13 05:31:01

+0

添加了驗證的建議,但我個人會選擇Dave的解決方案。 – Mischa 2010-10-14 01:39:17

3

這裏是實現這一目標的一個可能的更多的Rails-Y方式。這是我自己使Ruby模仿抽象類(Shop)行爲的味道。因人而異。

編輯:請注意,我用的繼承使用ActiveRecord的Single Table Inheritance,它採用了「類型」一欄基本實現同樣的功能替代從OP的示例中的「帳戶」變量,但使用繼承來表示不同類型的商店和他們各自的產品限制。 OP的原始示例可能違反 Liskov Substitution Principle,STI是解決該問題的一種方法。

編輯:好像我沒有足夠迂腐,從技術上講,它不是一個真正的Liskov違反,就像Open/Closed violation。它們都是同一主題的變體。你明白了。

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 
+0

可隨時用downvote添加評論。你在這裏不喜歡什麼? – 2010-10-12 17:08:44

+0

請解釋下降。這裏的設計基本穩固。 OP正在嘗試使用rails,這是一個很好的OOP解決方案,可以引入STI和自定義驗證。我可以在這裏看到變化 - 使用班級變量而不是「限制」方法或其他可能性。但這個解決方案是完全有效的。 – 2010-10-12 17:43:10

+0

總之,沒有解釋,這些降價是無稽之談,OP應該考慮我的解決方案或一些變化作爲STI +自定義驗證的一個好例子。 ; p用'type'變量混淆模型會引起Liskov的憤怒。任何你想要使用'type'(或在這種情況下'帳戶')的業務規則的樞紐,你應該考慮多態(在傳統意義上)的解決方案。 – 2010-10-12 19:43:11

3

它不需要那麼辛苦:

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   

此驗證檢查每次保存。您不需要檢索「帳戶類型」,假設它是商店的一個領域。同樣,不要寫查詢來計算nr產品,而是使用size函數。

這是一個簡單的解決方案。 @Dave_Sims建議的STI解決方案是有效的並且更加面向對象。

0

嗯,首先感謝大家的大力幫助和有見地的討論。 :)

我從你的答案中抽出一些東西來組裝一個我能理解我自己的解決方案。看來當談到編程時,我只能理解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 

然後在products_controller.rb我把這些線:

def new 
if current_user.shop.nil? 
    flash[:notice] = I18n.t 'shops.create.must' #this should check is the user owns a shop, otherwise can't add a product. It seems to work, so far 
    redirect_to :action => :index 
elsif current_user.shop.valid? 
    flash[:notice] = I18n.t 'shops.upgrade.must' 
    redirect_to :action => :index 
    else 
    @product = Product.new 
    end 
end 

的店現在是1型和只有9個產品,但每當我點擊「我的新產品」鏈接我被重定向到/ productss.upgrade.must消息。

我不知道,看來

account 

in shop.rb不返回正確的值。該列是一個int(11)類型,所以我想它只能返回一個數字,但仍然...

+0

您的驗證測試是錯誤的。我不明白你在做什麼,因爲你的if塊中沒有代碼。您應該通過執行'errors.add_to_base'或'errors [:base] <<'來增加'errors'。 – nathanvda 2010-10-14 07:27:24

0

再次,感謝您的巨大支持。我結束了從您的解決方案中偷取位並執行此代碼:

#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 

它似乎工作到目前爲止。希望我沒有犯任何明顯的錯誤。

再次感謝! :)