Rails 联合验证 table
Rails validation on a joint table
我有以下型号:
产品(id,名称):
has_many :prices
Product_price (id, product_id, price): 每个产品可以有不同的价格
belongs_to :product
订阅(id,名称):
has_many :subscription_price_sets,
foreign_key: :subscription_price_set_id,
inverse_of: :subscription
has_many :product_prices, through: :subscription_price_sets
Subscription_price_set (id, product_price_id, subscription_id):
belongs_to :subscription,
foreign_key: :subscription_id
belongs_to :product_price,
foreign_key: :product_price_id
如何验证它,以便对于给定的订阅,不可能有两种不同价格的产品?
例如:
我有两个产品:Notebook (id: 1) 和 Pencil (id: 2)
他们的价格是:
Product_prices:
(id: 1, product_id: 1, price: 4)
(id: 2, product_id: 1, price: 12)
(id: 3, product_id: 1, price: 10)
(id: 4, product_id: 2, price: 3)
(id: 5, product_id: 2, price: 2)
以及基本订阅:
(id: 1, name: "Basic")
假设我有 Subscription_price_set:
(id: 1, product_price_id: 1, subscription_id: 1)
现在我应该可以用 subscription_id: 1
创建另一个 Subscription_price_set
,但是唯一允许的 product_price_id 应该是 id: 4 和 id: 5.
关于如何实现的任何提示?
使用scope
对多列进行唯一性验证:
validates_uniqueness_of :subscription_id, scope: :product_price_id
然而,这实际上并不能保证唯一性。
为了防止竞争条件,您需要使用数据库索引补充验证:
class AddIndexToSubscriptionPriceSets < ActiveRecord::Migration[6.0]
def change
add_index :subscription_price_sets, [:subscription_id, :product_price_id] , unique: true
end
end
你还使用了 foreign_key
选项全错了。 Rails 由约定优于配置驱动,将从关联名称派生外键。如果关联名称不匹配,您只需要指定 foreign_key
。
belongs_to :subscription
belongs_to :product_price
在has_many
关联上它实际上会导致错误:
has_many :subscription_price_sets,
foreign_key: :subscription_price_set_id,
inverse_of: :subscription
这将导致以下加入
JOINS subscription_price_sets ON subscription_price_sets.subscription_price_set_id = subscriptions.id
当然会爆炸,因为没有这样的专栏。 has_many 关联上的 foreign_key 选项用于指定另一个 table 上的哪一列对应于此 table。您真正需要的是:
has_many :subscription_price_sets
Rails 也可以推导出基于关联的倒数,你只需要指定当你是 "going off the rails" 并且名称不匹配时。
我在 Subscription_price_set
模型中创建了一个自定义验证方法,它成功了:)
validate :product_uniqness
private
def product_uniqness
return unless subscription.product_prices.pluck(:product_id)
.include?(product_price.product_id)
errors.add(:product_price_id, 'You can\'t add the same product twice')
end
我有以下型号:
产品(id,名称):
has_many :prices
Product_price (id, product_id, price): 每个产品可以有不同的价格
belongs_to :product
订阅(id,名称):
has_many :subscription_price_sets,
foreign_key: :subscription_price_set_id,
inverse_of: :subscription
has_many :product_prices, through: :subscription_price_sets
Subscription_price_set (id, product_price_id, subscription_id):
belongs_to :subscription,
foreign_key: :subscription_id
belongs_to :product_price,
foreign_key: :product_price_id
如何验证它,以便对于给定的订阅,不可能有两种不同价格的产品?
例如:
我有两个产品:Notebook (id: 1) 和 Pencil (id: 2) 他们的价格是:
Product_prices:
(id: 1, product_id: 1, price: 4)
(id: 2, product_id: 1, price: 12)
(id: 3, product_id: 1, price: 10)
(id: 4, product_id: 2, price: 3)
(id: 5, product_id: 2, price: 2)
以及基本订阅:
(id: 1, name: "Basic")
假设我有 Subscription_price_set:
(id: 1, product_price_id: 1, subscription_id: 1)
现在我应该可以用 subscription_id: 1
创建另一个 Subscription_price_set
,但是唯一允许的 product_price_id 应该是 id: 4 和 id: 5.
关于如何实现的任何提示?
使用scope
对多列进行唯一性验证:
validates_uniqueness_of :subscription_id, scope: :product_price_id
然而,这实际上并不能保证唯一性。
为了防止竞争条件,您需要使用数据库索引补充验证:
class AddIndexToSubscriptionPriceSets < ActiveRecord::Migration[6.0]
def change
add_index :subscription_price_sets, [:subscription_id, :product_price_id] , unique: true
end
end
你还使用了 foreign_key
选项全错了。 Rails 由约定优于配置驱动,将从关联名称派生外键。如果关联名称不匹配,您只需要指定 foreign_key
。
belongs_to :subscription
belongs_to :product_price
在has_many
关联上它实际上会导致错误:
has_many :subscription_price_sets,
foreign_key: :subscription_price_set_id,
inverse_of: :subscription
这将导致以下加入
JOINS subscription_price_sets ON subscription_price_sets.subscription_price_set_id = subscriptions.id
当然会爆炸,因为没有这样的专栏。 has_many 关联上的 foreign_key 选项用于指定另一个 table 上的哪一列对应于此 table。您真正需要的是:
has_many :subscription_price_sets
Rails 也可以推导出基于关联的倒数,你只需要指定当你是 "going off the rails" 并且名称不匹配时。
我在 Subscription_price_set
模型中创建了一个自定义验证方法,它成功了:)
validate :product_uniqness
private
def product_uniqness
return unless subscription.product_prices.pluck(:product_id)
.include?(product_price.product_id)
errors.add(:product_price_id, 'You can\'t add the same product twice')
end