Pt-G-3
Cliff Says
Here’s a stab at it. Generate a PaymentType model with a single string field.
rails generate model payment_type name:string
Add the three payment types in seeds.rb, and run rake db:seed.
PaymentType.create(:name => "Check")
PaymentType.create(:name => "Credit card")
PaymentType.create(:name => "Purchase order")
Add a names class method to the PaymentType model.
class PaymentType < ActiveRecord::Base
def self.names
all.collect { |payment_type| payment_type.name }
end
end
Change the validation in the Order model. Initially, one might do this:
validates :pay_type, :inclusion => PaymentType.names
Now I’m a Rails n00b, but as far as I can tell, doing validation that way would fetch the payment types just once from the DB at the moment when Ruby defines the Order model class. What if the payment types table in the DB is modified after the Order model class has been defined? Hence, I rewrote the validation to query the DB for the payment types every time the validation is called (at least, I think I did):
validates_each :pay_type do |model, attr, value|
if !PaymentType.names.include?(value)
model.errors.add(attr, "Payment type not on the list")
end
end
Then, in views/orders/_form.html.erb, replace
<%= f.select :pay_type, Order::PAYMENT_TYPES,
:prompt => 'Select a payment method' %>
with
<%= f.select :pay_type, PaymentType.names,
:prompt => 'Select a payment method' %>
Wham bam. Added some unit and functional tests too. Works fine. I’m not super satisfied with the messy-looking validates_each block, though. Anyone with a more elegant validation?
Older says (11/09/08)
Probably the best way to avoid later problems in case there is some payment method chances, would be to link an order’s payment method to the payment method’s id. This way, we can always change payment method names without having to update older orders. Then, it would be nice to migrate and add payment_method.id to order. Edit: I tried to but did not go well. I guess there is a way to deal with catalog table I don’t know yet.
Ernesto says (14/09/2011)
rails generate migration add_pay_type_id_to_order pay_type_id:integer
rails generate scaffold pay_type name:string
rake db:migrate
than:
order.rb
has_one :pay_type
validates :pay_type_id, presence: true
pay_type.rb
belongs_to :orde
in app/views/orders/_form.html.erb
<div class="field">
<%= f.label :pay_type_id %><br />
<%= collection_select(:order, :pay_type_id, PayType.all, :id, :name) %>
</div>
You also need to edit the other views in app/views/orders….
more informations about: http://guides.rubyonrails.org/form_helpers.html starting at 3.2
Pepe says (26/09/2011)
Ernesto solution have some errors in order and pay_type models using has_one and belongs_to rails methods.
belongs_to means that the foreign key is in the table for this class. So belongs_to can ONLY go in the class that holds the foreign key.
has_one means that there is a foreign key in another table that references this class. So has_one can ONLY go in a class that is referenced by a column in another table.
So correct setting are:
order.rb
belongs_to :pay_type
validates :pay_type_id, presence: true
pay_type.rb
has_many :order
In app/views/orders/index.html.rb
<td><%= order.pay_type.name %></td>
Pepe says (28/05/2012) Well I also implemented migration code that creates the new PaymentType table and migrates all the existing data using the new data model.
def up
create_table :payment_types do |t|
t.string :name
t.timestamps
end
PaymentType.create(name: 'Check')
PaymentType.create(name: 'Credit Card')
PaymentType.create(name: 'Purchase Order')
add_column :orders, :payment_type_id, :integer
Order.reset_column_information
Order.all.each do |order|
order.payment_type_id = case order.pay_type
when 'Check'
1
when 'Credit Card'
2
when 'Purchase Order'
3
end
order.save validate: false
end
Order.reset_column_information
remove_column :orders, :pay_type
end
def down
add_column :orders, :pay_type, :string
Order.reset_column_information
Order.all.each do |order|
order.pay_type = case order.payment_type_id
when 1
'Check'
when 2
'Credit Card'
when 3
'Purchase Order'
else
'Credit Card'
end
order.payment_type_id = 0
order.save validate: false
end
Order.reset_column_information
drop_table :payment_types
remove_column :orders, :payment_type_id
end

