Tengo los siguientes modelos:

Producto (id, nombre):

    has_many :prices

Product_price (id, product_id, price): La cuestión es que cada producto puede tener precios diferentes

    belongs_to :product

Suscripción (id, nombre):

    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, Subscribe_id):

    belongs_to :subscription,
               foreign_key: :subscription_id
    belongs_to :product_price,
               foreign_key: :product_price_id

¿Cómo lo valido, de modo que para una suscripción determinada es imposible tener un producto con dos precios diferentes?

Por ejemplo:

Tengo dos productos: Notebook (id: 1) y Pencil (id: 2) Y sus precios son:

Precios del producto:

(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)

Y una suscripción básica:

(id: 1, name: "Basic")

Digamos que tengo Subscription_price_set:

(id: 1, product_price_id: 1, subscription_id: 1)

Ahora debería poder crear otro Subscription_price_set con subscription_id: 1, pero los únicos product_price_ids permitidos deberían ser id: 4 e id: 5.

¿Alguna pista sobre cómo lograr eso?

0
Tommy5230 15 abr. 2020 a las 12:21

2 respuestas

La mejor respuesta

Creé un método de validación personalizado en el modelo Subscription_price_set, y funcionó :)

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
1
Tommy5230 10 jun. 2020 a las 15:00

Utilice scope para realizar una validación de unicidad en varias columnas:

validates_uniqueness_of :subscription_id, scope: :product_price_id

Sin embargo, esto en realidad no garantiza la unicidad.

Image courtesy of Thoughtbot

Para protegerse contra las condiciones de carrera, debe complementar la validación con un índice de base de datos:

class AddIndexToSubscriptionPriceSets < ActiveRecord::Migration[6.0]
  def change
    add_index :subscription_price_sets, [:subscription_id, :product_price_id] , unique: true
  end
end

También estás usando la opción foreign_key, todo está mal. Rails se maneja por convención sobre la configuración y derivará la clave foránea del nombre de la asociación. Solo necesita especificar foreign_key si el nombre de la asociación no coincide.

belongs_to :subscription
belongs_to :product_price

En la asociación has_many en realidad causará un error:

has_many :subscription_price_sets,
         foreign_key: :subscription_price_set_id,
         inverse_of: :subscription

Esto dará como resultado la siguiente unión

JOINS subscription_price_sets ON subscription_price_sets.subscription_price_set_id = subscriptions.id

Lo cual, por supuesto, explotará ya que no existe tal columna. La opción Foreign_key en una asociación has_many se usa para especificar qué columna en la otra tabla corresponde a esta tabla. Todo lo que realmente necesitas es:

has_many :subscription_price_sets

Los rieles también pueden deducir el inverso de una asociación basada y solo necesita especificar cuándo está "saliendo de los rieles" y los nombres no coinciden.

1
max 15 abr. 2020 a las 13:03