Reputation: 3905
I have what I imagine must be a common situation, but possibly can't find the way to phrase the question to get the solution...
I have a couple of linked models in my application:
class Product < ActiveRecord::Base
validates_uniqueness_of :prod_code
end
class Stock < ActiveRecord::Base
belongs_to :product
end
In fact there are a number of other models which also belong to Product. By default, on the stock record, I just see the product_id field, which is an auto-incrementing number that isn't much help to the user. Products have a unique prod_code
which is on barcodes etc. and is the natural key of the product database.
What I would like is for the create/edit screens for the stock and other linked models to show a text field for the prod_code, and to be able to respond to parameters in the form stock[prod_code]
in a sensible way (e.g. look up the prod_code, and set the prod_id based on the result), and automagically (e.g. Stock.new(params[:stock])
should work.
To clarify, setting stock[prod_code] would not change anything in the product database; it would instead change the product_id for the relevant stock record i.e. link the stock record to a different product record.
At present, I've got various methods defined in the stock model such as prod_code=
that make this work. But as I mentioned, there are actually multiple models that refer back to my products table. Is there any way I can define something inside the Product model?
e.g. something like a referenced_by method that would tell all linked models to deal with the prod_code
argument by looking it up in the products table?
class Product < ActiveRecord::Base
validates_uniqueness_of :prod_code
referenced_by :prod_code
end
Upvotes: 0
Views: 2297
Reputation: 970
You could use prod_code instead of product_id as the foreign_key:
class Stock
belongs_to :product, :foreign_key => "prod_code"
end
Or you can set it on Product:
class Product
has_many :stocks, :foreign_key => "prod_code"
has_many :images, :foreign_key => "prod_code"
has_many :sizes, :foreign_key => "prod_code"
end
Upvotes: 0
Reputation: 1233
What you probably want to use are nested forms.
First you can tell your model to accept the nested attributes (api for nested attributes),like
accepts_nested_attributes_for :product
in your stock model.
Then in your form you can use fields_for (api for fields_for), to nest product fields into your stock form. Imagine something like:
= form_for(@stock) do |form|
= form.text_field :some_stock_attribute
= form.fields_for(@stock.product) do |product_form|
= product_form.text_field :prod_code
This essentially nests the name of your prod_code form field, to "stock[product_attributes][prod_code]", while, thanks to the accepts_nested_attributes_for, your stock model is prepared to pass the product_attributes to the associated product.
A referenced_by method as you imagine, does not exist. Also you will have to take care of a bit more, then just changing the associated product. You will still want to display an error, if the prod_code doesn't exist, i assume.
Roughly you can add a virtual attribute to the stock, which allows your stock form to have a text_field :prod_code (add attr :prod_code to stock model).
Second, before validation you will probably need a method, that looks up the product for the prod_code, and changes the association, or adds an error on :prod_code.
this could look a bite like this:
class Stock
attr :prod_code
before_validation :associate_product_from_prod_code
def associate_product_from_prod_code
unless self.product = Product.where(:prod_code => prod_code)
errors[:prod_code] << "Product Code is not valid"
end
end
end
Upvotes: 2