Thursday, 10 July 2008

The Model Part 2 - Validation and Association

Last time around I was looking at what the model is, now I will look at the actual code.

The model class file could be as simple as:

class MyModel < ActiveRecord::Base

end


The superclass, Base in module ActiveRecord, handles all the SQL and more besides, and for simple applications that is all you might need. Often you want more, though, and there are broadly three things you might include. These are validation, association and actual ruby code to define your class behaviour.

Validating input
There are several validating methods available. They are used like this:
validates_presence_of :attribute1, :attribute2

By including one or more of these in your model, you will ensure that all data going into the model is valid, according to that requirement. In the example above, Rails will only allow the model to be updated with the new data if both attribute1 and attribute2 are present. Other options:
validates_acceptance_of
validates_associated
validates_confirmation_of
validates_each
validates_exclusion_of
validates_format_of
validates_inclusion_of
validates_length_of
validates_numericality_of
validates_presence_of
validates_size_of
validates_uniqueness_of

For the most part they are very easy to use. Here, the field is only valid if there is no record with the same value for that field already in the database:
validates_uniqueness_of :sample_reference

A bit more complex, this will check the field conforms to a certain pattern:
validates_format_of :sample_reference, :with => /^(P|L)\d\d\d\d\d$/

You can also do it all yourself. This example ensures there are no question marks in the description field:
validate do |sample|
sample.errors.add(:description, "replace ?") if sample.description.include? '?'
end

Note that errors go to the errors object in your record. Use add to attach the error to a specific attribute, or add_to_base otherwise.

Also note that update_attributes (used by the update method in your controller) also checks validation, returning false if validation fails. It may not be give any clue about why it failed (which can lead to very mysterious errors during testing).

Another point; this is a block, not a method that you are defining. If you use return in there, it will give some strange behaviour as Ruby attempts to return from the method that invoked the block (at least, I assume that is why you get the strange behaviour).

For more details go here:
http://railsapi.org/ActiveRecord::Validations::ClassMethods


Associations
Associations are a way to connect two or more tables in a database. They are done through class methods in Activeecord::Base, just like validations. Associations can be many-to-one, one-to-one or many-to-many.

Let us suppose we have two models - model1 and model2 - where model2 can be associated with any number of model1. By convention, model1 will have a column model2_id.

model1
In the model definition you need this to associate it with model2:
belongs_to :model2
You can now access model2 like this.
model1.model2

model2
In the model definition you need this to associate it with model1:
has_many :model1s # note the plural
You can now access model1 like this.
model2.model1s

model1s is an array (effectively), and you can use and manipulate it in the normal way (but see here for a neat filtering trick that you can not do on a real array).

For a one-to-one relationship, you can use this:
has_one :model1

Again, model1 will need a field model2_id for this to work. Having said that, model2_id is just the default name. You can use your own name with the foreign_key option.
has_many :model1s, :foreign_key => 'id_of_model2'

I would guess you need that for both the has_many and the belongs_to?

Reference:
http://en.wikibooks.org/wiki/Ruby_on_Rails/ActiveRecord/Associations
http://www.jumbabox.com/2008/06/ruby-on-rails-one-to-many-tutorial/

through
A through association allows model2 to access model0 via model1
has_many :model1
has_many :model0, :through => model1
Now you can now access model0 like this.
model2.model0s

Reference:
http://wiki.rubyonrails.org/rails/pages/ThroughAssociations

Note: There are issues with validation if the parent has not been saved
http://macksmind.net/2008/04/26/using-belongs_to-in-rails-model-validations-when-the-parent-is-unsaved/

polymorphic
The polymorphic option allows you to associate a model with different classes of models, either though the class inheritance (i.e., model1 can be associated with model2 or any of its subclasses) or using the "as" option.

The "as" option sets up an interface (in the background; no need for you to worry about that):
belongs_to :my_interface, :polymorphic => true
has_many :model1s, :as => :my_interface

many-to-many
I have yet to try this, but here is an interesting page:
http://www.jumbabox.com/2008/06/ruby-on-rails-many-to-many-tutorial/

Ruby Code
The last thing you might want to put in your model (that I know about!) is Ruby code to define the behavior of the object. There is an issue here about what goes in the model and what goes into the controller. Methods invoking the object go in ther model, methods involved in controlling the web site go into the controller. In general, I try to put everything I can into the model, as it is easier to test.

Struggling with Ruby: Contents Page

1 comment:

Admiral Adney said...

I was seeking for ror development and set down up on your mail and i should state thanks for distributing such helpful information.