Forum: Ruby on Rails Using 'validates_inclusion_of' to validate foreign key

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Michael G. (Guest)
on 2006-05-27 00:30
I seem to be missing something trying to use 'validates_inclusion_of' to
validate a foreign key and was hoping some one could piont out my
mistake?

The problem seems to be that Order.find_all.collect is not returning an
array that contains the order_id, if I replace it with a hardcoded array
everything works as expected.

The model:

class OrderItem < ActiveRecord::Base
  validates_inclusion_of :order_id, :in => Order.find_all.collect {
|order| order.id }
  belongs_to :order
end

The test:

class OrderItemTest < Test::Unit::TestCase
  fixtures :orders, :order_items
  def test_validates_inclusion_of_order_id_in_orders
    orderitem = OrderItem.new(:order_id => 1)
    assert( orderitem.save, orderitem.errors.full_messages.join('\n'))
  end
end

The fixture:
first:
  id: 1
  sid: 'ON001'

All of the code can be found at http://metaspot.net/dev/browser/?rev=46
Michael G. (Guest)
on 2006-05-27 01:41
Michael G. wrote:
> The problem seems to be that Order.find_all.collect is not returning an
> array that contains the order_id, if I replace it with a hardcoded array
> everything works as expected.
>

Guess I'll respond to my own post...

This was most likley a bad idea anyway.  I really don't want to suck an
entire table index into an array every time I want to create or update a
new row.  I should of just created a validation that did something like
this....

 def validate
    begin
      Order.find(self.order_id)
    rescue ActiveRecord::RecordNotFound
      errors.add(:order_id, "invalid foreign key reference")
    end
  end
Jonathan V. (Guest)
on 2006-05-27 01:49
(Received via mailing list)
You might have difficulty using :in => to achieve that. A custom
validation is probably in order. For performance reasons, it's
probably worth adding a method to ActiveRecord::Base to select just
the model ids. That will save you from having to instantiate all the
model objects each time the validation occurs.

class ActiveRecord::Base
  def self.ids
    connection.select_values("select #{primary_key} from
#{table_name}").collect(&:to_i)
  end
end

class OrderItem
  belongs_to :order

  def validate
    errors.add_to_base "Invalid order" unless
Order.ids.include?(order_id)
  end
end

-Jonathan
Jonathan V. (Guest)
on 2006-05-27 04:02
(Received via mailing list)
That is probably a better approach. Using Order.exists? will make the
code a bit shorter:

errors.add_to_base 'Invalid order' unless Order.exists?(order_id)

-Jonathan.
Michael G. (Guest)
on 2006-05-27 06:06
Jonathan V. wrote:
> That is probably a better approach. Using Order.exists? will make the
> code a bit shorter:
>
> errors.add_to_base 'Invalid order' unless Order.exists?(order_id)
>
> -Jonathan.

Much better, it's starting to look a bit more like ruby... now that it's
been expressed in a single line ;)

Thanks
This topic is locked and can not be replied to.