chapter 4 - activerecord - the model part of mvc

32
Chapter 4 – ActiveRecord Chapter 4– ActiveRecord – The Model part of MVC. ActiveRecord It’s an implementation of a design pattern ‘ Active Record ’ (observe the space in between), which is an Object Relational Mapping(ORM) mechanism. ActiveRecord is one such implementation of this design pattern which helps us do all our database tasks (Data Definition and Data Manipulation through its meta programming capabilities) without we having to write any SQL statement. It represents database tables through classes and instances of those classes. A class which wraps around a database table is called a “model”, instances of these classes are “model instances”. Models encapsulate business logic Some more examples of ORM implementation are ‘Hibernate’ for Java and ‘Castle’ in .Net Though ActiveRecord is a ruby library built for Rails, it can be used with other frameworks as well. But we will stick to rails for now. Just for the information ActiveRecord source code is the biggest part of Rails total source. ActiveRecord library supports almost all popular databases such as Oracle, sqlServer, db2, postgresql, sqlite, Sybase etc. What happens underneath the ActiveRecord (AR): 1. generates sql 2. the sql is passed to underlying RDBMS 3. the RDBMS compiles this SQL 4. then executes it, 5. returns an enumerable (array like object) 6. creates an ActiveRecord model object for each row. As one can see, the whole process of SQL execution would be slower as compared to running a ‘stored procedure’ in the Rails 2.0 Notes: Sunil Kelkar [email protected]

Upload: pratikkelkar

Post on 13-Nov-2014

1.223 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Chapter 4– ActiveRecord – The Model part of MVC.

ActiveRecord

It’s an implementation of a design pattern ‘Active Record’ (observe the space in between), which is an Object Relational Mapping(ORM) mechanism.

ActiveRecord is one such implementation of this design pattern which helps us do all our database tasks (Data Definition and Data Manipulation through its meta programming capabilities) without we having to write any SQL statement. It represents database tables through classes and instances of those classes.

A class which wraps around a database table is called a “model”, instances of these classes are “model instances”. Models encapsulate business logic

Some more examples of ORM implementation are ‘Hibernate’ for Java and ‘Castle’ in .Net

Though ActiveRecord is a ruby library built for Rails, it can be used with other frameworks as well. But we will stick to rails for now. Just for the information ActiveRecord source code is the biggest part of Rails total source.

ActiveRecord library supports almost all popular databases such as Oracle, sqlServer, db2, postgresql, sqlite, Sybase etc.

What happens underneath the ActiveRecord (AR):1. generates sql2. the sql is passed to underlying RDBMS3. the RDBMS compiles this SQL4. then executes it,5. returns an enumerable (array like object)6. creates an ActiveRecord model object for each row.

As one can see, the whole process of SQL execution would be slower as compared to running a ‘stored procedure’ in the database (which is a pre-compiled code). One can always ask, can we run a ‘stored procedure’ from AR directly?, yes, we can. But as stored procedures are relational database specific, this could become a limitation.

We can(and often need to) run SQL directly from AR to speed up the process But then one has to learn writing good SQL queries and implement ANSI SQL so as to make our application RDBMS agnostic.

Rails 2.0 Notes: Sunil Kelkar [email protected]

Page 2: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Before we dive-in learning the nuts and bolts of AR, please make a note of the following points:

1. ActiveRecord is a Model layer, an interface which helps us manages our database. All the CRUD (Create, Read, Update, Delete) operations can be to be done through AR. The Relational database can be used merely as a record storage purpose.

2. The code we write here would consist of most of the business logic. Obviously it is very much likely to be reusable across applications.

3. While working on any application, one should analyze, how far our application needs to be database agnostic, when to utilize the power of flexibility and when do you need the speed of execution and when to do the balancing act.

4. ActiveRecord::Migration class is used for Data Definition purposes and ActiveRecord::Base class is used for Data Manipulation purposes.

I plan to cover following points in this chapter.

1. AR-database connectivity.2. Conventions over configuration3. Creating a Model and various CRUD features available in AR.4. Validations5. Callbacks6. Model Associations. One-to-one, One-to-many (w/o eager loading), many-to-many

(using has-and-belongs-to-many and :through options)

Rails 2.0 Notes: Sunil Kelkar [email protected]

Note : 1. We will be creating/altering our relational database schema through migrations (see Chapter 5). But for the sake of keeping focus only on AR ‘model’, we create tables in mySQL directly.

2. In this chapter we will be using ‘ruby script/console’ extensively. http://wiki.rubyonrails.org/rails/pages/Console The console gives you access to your Rails Environment where you can interact with the domain model directly. You can inspect domain models, change values, and save to the database. Use ‘exit’ command to exit from the console window.

Page 3: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

1 How AR connects itself to the database

As we have seen earlier in chapter 3, the file ‘database.yml’ has all configuration settings associated with the database, which ActiveRecord uses to connect to.

<environment>: adapter: mysql encoding: utf8 database: sample1_development username: root password :root host: localhost

ActiveRecord can connect itself to different databases. The appropriate adapters are required to connect to them.

The added separation of environment (Development/Test/Prod.) with ActiveRecord as an ORM tool, gives developers a flexibility to test/deploy there application on different databases

One would wonder why rails prefers to use .yml files over XML. The reason is simplicity readability (human readable) and usability of the .yml structure over XML.

Observe the two formats having the same contents:

.XML File .YML File<user id="boby" on="cpu1">   <first_name>Sunil</first_name>   <last_name>Kelkar</last_name>   <user_name>svk2006</user_name></user>

  boby:    computer: cpu1    first_name: Sunil    last_name: Kelkar user_name: svk2006

Pl observe the .yml file as a ‘key – value’ pair. Ruby syntax can very easily convert this file to a hash table and carry out the connectivity. http://yaml4r.sourceforge.net/cookbook/ is a great source for those interested in knowing all about YML files.Rails uses YAML files extensively to specify fixtures which we will see in the coming sessions..

ActiveRecord::Base.establish_connection is the method which establishes the connection to the database. It accepts a hash as input where the :adapter key must be specified with the name of a database adapter (in lower-case)

ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "localhost", :username => "root", :password => "root", :database => "sample1" )

Rails 2.0 Notes: Sunil Kelkar [email protected]

Development, Test or Production

Page 4: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

2. Conventions over configuration:

For the magic of ActiveRecord to work, you must name our database tables and columns according to specific conventions.

Model class name should be in CamelCase and is mapped to the Table which is a pluralized form of the model class name.

E.g. If the model name is ‘User’, rails would assume the table name is ‘users’ (table name is lower case). If our model class is ‘Person’, mapping table name would ‘people’, a pluralizing of the word ‘Person’. (Tables are ‘plural’ and the associated model is ‘singular’)

How does rails knows this? With the help of ‘Inflector’ class, we get the answer.

B. Primary Key name should always be ‘id’, which should be an auto-incremented integer, case sensative.

C. Foreign keys join database tables. When you need a foreign key field in a table, the key takes the form "{singular foreign table name}_id". E.g. person_id with an English singular and an _id suffix.

D. If you have fields “created_on” or “created_at” in your table, AR will automatically timestamp them on initial row create.IF you have fields “updated_on” or “updated_at” in your table, AR will automatically timestamp them whenever a modification is made to the record. Use ‘datetime’ as the column type, not timestamp. These fields should not have default values, rails takes care of that for you. These fields should be able to be set to NULL

E. For association tables (also known as intersection tables) that resolve many-to-many relationships, we use the following naming convention: {name of first entity}_{name of second entity} e.g. products_categories ( explained with example later in the chapter)

.

Rails 2.0 Notes: Sunil Kelkar [email protected]

c:\sample1> ruby script/console Loading development environment.

>> Inflector.pluralize('test')=> "tests">> Inflector.pluralize('mouse')=> "mice">> Inflector.pluralize('person') => "people">> "company".pluralize=> companies

Page 5: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Create a Model:

Lets us carry out one simple exercise to prove our point. 1. We need to create one table (users) and one model (User)2. The rails convention will automatically map model (User) on the table (users).3. We will use the ‘console’ script to add data to this table without writing any SQL query.

1. Pl create one table in mySql with the following structure (do not worry about the size of individual fields right now, take the defaults)

users

id integer

first_name string

last_name string

user_name string

login string

password string

phoneNo integer

2) We create a model called ‘User’:

Rails 2.0 Notes: Sunil Kelkar [email protected]

railsApps\sample1> ruby script/generate model User

Note: This convention adds to standardization which in turn adds to the speedy development. But still, one would wonder, what if one has to bypass/override these conventions, due to some practical problems(say, incase of some tables already exists).Yes, there is a way. I have listed some overrides at the end of this chapter.

Page 6: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

You should get the following output:

You will observe, no. of folders and files getting created. Do not bother too much about ‘test’, ‘app’ and ‘db’ folders/files right now. Let’s just focus on the app/models folder, and ‘user.rb’ file.

ActiveRecord::Base is the class which all models inherit. This inheritance givesour User class, a tremendous amount of functionality. As we learn further we will realize that these model classes will contain all the business logic of our applications.

In this case, ‘User’ class automatically maps to ‘users’ tables as per rails conventions.

3) Now let’s play around our model, ‘User’:

Rails 2.0 Notes: Sunil Kelkar [email protected]

railsApps\sample1> ruby script/console Loading development environment.

user.rb

class User < ActiveRecord::Base

end

Page 7: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Objects ‘user1’ and ‘user2’ are mapped on two records of ‘users’ table. While we are executing these simple instructions, underneath, AR is doing the magic of generating/executing the SQL statements. These operations are logged in a log file.

A note on Logging and Log Files

Please open /log/development.log file in a separate window and find the SQL generated.

Rails has logging built right into the framework, it exposes a Logger object to all the code in a Rails application. Whenever we execute operations in AR, it generates a log of activities.Logs help us monitor/manage our application behavior/performance.

A development environment will log to ‘log/development.log’, an application under test to ‘log/test.log’, and a production application to ‘log/production.log’.

We can generate log messages at the warning, info, error, and fatal levels.

log/development.log:

Rails 2.0 Notes: Sunil Kelkar [email protected]

>> user1 = User.new>> user1.first_name = "Satish">> user1.last_name = "Talim">> user1.email = "[email protected]">> user1.save=>true

>> user2 = User.create(:first_name => ‘Marcos’ , :last_name => ‘Ricardo’)  =>true

# Notice the difference between New and Create# The ‘new’ method, creates a new record in memory followed by a ‘save’ method to save it to the database where as ‘Create’ not only generates but saves a new record to the database.

>> user.update_attributes(:first_name => ‘Luiz’, :last_name => ‘Biagi’ )==>true

# You can go on adding / updating few more records this way.

Page 8: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

If you observe these SQL statement generated, it has some cryptic sequence of characters known as color escape sequences that are used by default. Simply set colorize_logging to false to make it more readable output:

Or better still, open config/environment.rb file and at the end of the file(last line), you type the above statement and save the file. We need to ‘exit’ from the console window since we have changed the settings and enter again to see the effect.

Now that we have done the log file setting, let’s continue with the model discussions.

Rails 2.0 Notes: Sunil Kelkar [email protected]

ActiveRecord::Base.colorize_logging = false

Note: After every ruby console command execution, continue to refresh this log file and observe the SQL generated by ActiveRecord.

Page 9: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Use Finder methods to retrieve records from the database:

Note: find(:all) would return an array like object which we can iterate using ‘each’ method.I hope you get some rough idea of, when a request from the browser is made, how the model retrieves the desired records from the database.

One can use SQL directly:

Dynamic Finder methods

Look at the method ‘find_by_first_namel’. ActiveRecord adds a dynamic finder for each of the attributes available in the table.

Rails 2.0 Notes: Sunil Kelkar [email protected]

>>User.find(1)  # find selects by 'id' (default integer primary key) >># find with ids 2, 4, and 7  >> User.find(1,4,7)

>> user = User.find(:all) >> user.each{ |rec| puts rec.first_name}>> user1 = User.find(:first) #will fetch you the first record

>> User.find_by_sql “SELECT usr_name, email FROM users”

A Tip: Dynamic finder methods

While using something like User.find_by_user_name is very readable and easy, it is an overhead to rails, ActiveRecord dynamically generates these methods within method missing and this can be quite slow.Using MyModel.find_by_sql directly, or MyModel.find, is more efficient.

>> User.find_by_first_name ‘Satish’

Page 10: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Finding records based on ‘conditions’

Custom Methods

Just observe the custom methods carefully. No where we are writing

any ruby statements to display the records. That’s the job of ‘Views’ which we will learn later. I can use the same class for multiple web applications as this class can become generic. ‘Reusability’ is the core of Object Orientated Programming.

Validations

Adding validations to our code means, the record won't be created or saved unless these conditions are met or satisfied. Model-based validations help make data within our database stays consistent.

Change ‘user.rb’ as shown below:

Rails 2.0 Notes: Sunil Kelkar [email protected]

>> User.find :all, :conditions => “first_name LIKE ‘S%’ ” , :limit => 10

Tip:Optimize on execution time. We should retrieve only the information that we need. A lot of execution time can be wasted by running selects for data that is not used.

Explore ‘:select’ option (extract only those fields we are going to use) , :limit and :offset options (if the record size is large, this option is a must)

user.rb

class User < ActiveRecord::Base

def full_name name = first_name + ‘ ‘ + last_name end

def self.find_all_ordered find :all, :order => ‘last_name, first_name’ end

end

Page 11: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Observe the validation validates_numericality_of with :allow_nil. It skips validation if attribute is nil (default is false). Note that for fixnum and float columns empty strings are converted to nil.

Since we have modified the User.rb, to reload the class, exit from the console and re-enter the console window.

An Errors object is automatically created for every Active Record.

For more samples and various validation options, a further reading is advised http://rails.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.html

Rails 2.0 Notes: Sunil Kelkar [email protected]

>> user = User.new>>User.new(:phoneNo => nil).valid? => true>> user.save>> false

User.rb

class User < ActiveRecord::Base

EMAIL_EXP = /^ [A-Z0-9._%-]+@[A-Z0-9.-]+\ . [ A-Z] {2,4} $/i validates_format_of :email, :with => EMAIL_EXP validates_presence_of :user_name, :message => “User name can not be Blank” validates_uniquness of :user_name validates_numericality_of :phoneNo, :allow_nil => true

end

>> user.errors

User2 = User.new("first_name" => "David", "phoneNo" => "what?") User2.save # => false (and doesn't do the save) User2.errors.empty? # => false User2.errors.count

User2.errors.on "user_name" # => "User name can not be Blank" User2.errors.on "email" User2.errors.each_full {|msg| puts msg}

Page 12: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Callbacks

The callbacks are similar to triggers in relational databases.

In any of the business application, ‘validation’ and ‘authentication’ of a record could be different activities/processes. Validations could be just related to the filed levels, while authentication would be applied to the entire form. E.g. In any credit card transaction, we can add ‘validations’ to our model to check if name, card no, expiry date etc are entered properly, but the actual ‘authenticity’ of the card(getting a confirmation for the validity of the card ) needs to be carried out , before the actual transaction takes place. This ‘pre process’ can be done using callbacks. The ‘post process’ could be to send emails to the customer with the status of the transaction. Even this we call as Callbacks.

During the application life cycle, we come across the following events:

after_validation After all validation procedures are complete

after_validation_on_create After validation of new objectsafter_validation_on_update After validation of existing objects being

updatedbefore_validation Before the object is validatedbefore_validation_on_create Before validation of new objectsbefore_validation_on_update Before validation of existing objects being

updatedbefore_create Before the object is added to the database

before_destroy Before an object is destroyedbefore_save Before an object is saved to the database,

either by way of creation or an updatebefore_update Before an object is modified in the database

Calculations

Do you want to get maximums, minimums, averages, or sums for data in your tables? ActiveRecord’s Calculations make these all possible. Most can be customized pretty deeply with extra options, so go read about them.

http://api.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html

Rails 2.0 Notes: Sunil Kelkar [email protected]

Page 13: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Model Associations

We are aware that web applications contain bunch of tables and they are ‘associated’ (related) with each other. With ‘normalization’ as the key, we avoid redundancy. These associations are of different types e.g.one-to-one, one-to-many, many-to-many, polymorphic etc. ActiveRecord as of today, can not read these associations automatically, we as programmers, need to define them in models, so we call it Model Associations.

One-to-one Association

For the sake of example, pl assume, the user (or author), in our ‘users’ table can write at most one ‘article’. To set this association, we need to

1. Have 2 tables, ‘users’ and ‘articles’ (we already have ‘users’ table)2. We need 2 models ‘User’ and ‘Article’ to map on the above 2 tables (we already have

‘User’ model/class).3. Define the association between these models.4. See how the above association works in AR by adding/updating records in these tables.

1) Define table ‘articles’ (we already have ‘users’)

users

id integer

first_name string

last_name string

user_name string

login string

password string

Pl create the ‘articles’ table in mySql as described above. The ‘user_id’ column is our foreign key in ‘articles’ table (as per rails naming convention).

2) Now that the tables are set, we need our model ‘Article’ and in that we set the association.

Rails 2.0 Notes: Sunil Kelkar [email protected]

articles

id integer

user_id integer

title string

created_on datetime

railsApps\sample1> ruby script/generate model Article

Page 14: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

3) Edit both the model classes and add the following association methods

has_one :article and belongs to :user are the methods (not method definitions), that take symbol as a parameter. The belongs_to method adds an association, called user, to article,

Please note that, :articles is singular, since the association is of the form ‘has_one’, in case association is one-to-many, it would become (has_many :articles).

** Observe the SQL being generated in each case (use the log file)

Rails 2.0 Notes: Sunil Kelkar [email protected]

user.rb

class User < ActiveRecord::Base has_one :articleend

article.rb

class Article < ActiveRecord::Base

belongs_to :user # foreign key user_idend

railsApps\sample1> ruby script/console Loading development environment

>> a1 = Article.new>> a1.title = “Instant Ruby”>> user = User.find(1)>> user.articles << a1>> user.save>>user

Page 15: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

One-to-many Association

Consider a case where ‘One User can write many Articles’. This indicates, we have a one-to-many association.

In order to understand this association, we will be doing the following:

1. Create 2 tables, ‘users’ and ‘articles’ ( both tables are available)2. We need 2 models ‘User’ and ‘Article’ to map on the above 2 tables.3. Define the association between these models4. See how the above association works in AR by adding/updating records in these tables.

1) Define table ‘articles’ (we already have ‘users’)

users

id integer

first_name string

last_name string

user_name string

login string

password string

2) Its time to update our models. Ideally, I would destroy the previously created model and create a new one with the same name as :

Give the same treatment to ‘Article’ model.

This ensures all previously created references are deleted. This does *not* delete our tables.Now create both the models with “ruby script/generate model User”, same of Article.

3) Define the correct association between the models.

belongs to :user and has_many :articles are the methods (not method definitions) that take symbol as a parameter. The belongs_to method adds an association, called user, to article,

Please note that, :articles is plural, since the association is of the form ‘has_many’, in case association is one-to-one, it would become (has_one :article).

Rails 2.0 Notes: Sunil Kelkar [email protected]

articles

id integer

user_id integer

title string

created_on datetime

user.rb

class User < ActiveRecord::Base has_many :articlesend

article.rb

class Article < ActiveRecord::Base

belongs_to :userend

railsApps\sample1> ruby script/destroy model User

Page 16: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Check out the following in your ruby console: DO CHECK the sql statements generated after execution of these instructions too.

From the above example, its quite evident that the parent model instance (‘user’) gets the child model methods (‘articles’)

We can add/update records even this way

Finder methods work as before, but note the association.

** Observe the SQL being generated in each case (use the log file).

One more question arises, and that is when we delete a ‘user’ from the users table, ‘what happens to the ‘article records’, as there is an association, do they get deleted automatically? NO.

The solution is to explore the Help files and you get the way out, we use the dependency option , :dependent.

Rails 2.0 Notes: Sunil Kelkar [email protected]

>> a1 = Article.new>> a1.title = “Instant Ruby”>> user = User.find(1)>> user.articles << a1>> user.save>>user

railsApps\sample1> ruby script/console Loading development environment

>> user = User.find(1)>> user.articles.count>> user.articles

>> user.articles.create( :title => “The Ruby Way”)

user.rb

class User < ActiveRecord::Base has_many :articles, :dependent => :destroyend

Page 17: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

When you refresh the console, you would see the user ‘John’ and his all authored books are deleted from ‘articles’ table.

Eager loading

The ActiveRecord documentation says: “Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1.”

To cut this story short, suppose we want to fetch all articles of a specific user, with the current knowledge we would carry out operations this way.

You will notice that 2 queries are fired to fetch the result set. A better way to go about it using, eager loading, using :include option

In one query, we get the result set, did you observe the ‘LEFT OUTER JOIN’?

Rails 2.0 Notes: Sunil Kelkar [email protected]

>> u = User.create(:first_name => “John”, :last_name => “Alter”)>> u.articles.create(:title => “Core Java”)>>u.articles.create(:title => “Developing Java Applications”)>>u.articles>>u.destroy

>>user1 = User.find(:first, :include => :articles)>>user1.articles

>> user = User.find(:first)>> user.articles

Page 18: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

One thing, we need to remember though, ActiveRecord ignores the ‘:select’ directive if we use ‘:include’. This could be a little overhead. But there is always an alternative. Use ‘:joins’ option

The query generated for the ‘find’ is as follows:

SELECT users.first_name, articles.title FROM `users` left outer join articles on users.id = articles.user_id LIMIT 1

Rails 2.0 Notes: Sunil Kelkar [email protected]

>>user2 = User.find(:first,:select => “users.first_name, articles.title”, :joins => "left outer join articles on users.id = articles.user_id")

>>user2.first_name>>user2.title

Page 19: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Many-to-many Association (using has_and_belongs_to_many)

Continuing our model discussion, in practice, we know a ‘user’ (or author) can write ‘many articles’ and one ‘article’ can have ‘many users(or authors)’.

In design terms we call this as ‘many-to-many-association’. In order to set the above association, we shall

1. use a method has-and-belongs-to-many (or ‘habtm’ when we talk about it) 2. we need to create a new concatenation table (joining the names of the tables in the

alphabetical order), which should contain the foreign keys of the target tables. (conventions, you see).

So, please set your tables and the model classes as shown below.Note: Continue to observe and study the SQL getting generated in log files.

users

id integer

first_name string

last_name string

user_name string

login string

password string

Rails 2.0 Notes: Sunil Kelkar [email protected]

articles_users

user_id integer

article_id integer

articles

id integer

title string

created_on datetime

user.rbclass User < ActiveRecord::Base has_and_belongs_to_many :articlesend

article.rbclass Article < ActiveRecord::Base has_and_belongs_to_many :users end

>> author1 = User.create(:first_name => “Author – 1”)>> author2 = User.create(:first_name => “Author – 2”)

>> a1 = Article.create(:title => “Book – 1”)>> a2 = Article.create(:title => “Book – 2”)

>> author1.articles << a1>> author1.articles << a2

>> a1.authors << author2

Page 20: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

I am going to dive a little further and explain the same association but with a different database design and a model method option (:through).

Many-to-many Association (using :through)

we again need to modify our design a little bit, (Its an agile world, change is the only constant, you see)

We will carry out 2 things 1. Create one more table ‘user_articles’ and *NOT* users_articles.2 Create a model class ‘UserArticle’ and update the correct relationship among these models.

users

id integer

first_name string

last_name string

user_name string

login string

password string

Rails 2.0 Notes: Sunil Kelkar [email protected]

user_articles

user_id integer

article_id integer

articles

id integer

title string

created_on datetime

railsApps\sample1> ruby script/generate model user_article

user.rb

class User < ActiveRecord::Base has_many :user_articlesend

article.rb

class Article < ActiveRecord::Base

has_many :user_ articles end

user_article.rb

class UserArticle < ctiveRecord::Base belongs_to :user belongs_to :articleend

Page 21: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Note: The plural user_articles and *not* user_article

I am assuming, you have added few records in users and articles tables.

Assume we have to find out all the users( authors) for a specific article. Ideally we should get result in two steps viz>> a = Article.find(1) # the first article in the table.>> a.users # and this should fetch all the users for that article.

But with the above mapping, our steps would be

>> a = Article.find(1) # the first article in the table.>> a.user_articles # would fetch us an array, lastly>>a.user_articles[0].user # we get the actual user name/s

Did you see, we needed one extra steps to carry out the job? Did you observe the query generated, well it needs to be optimized too.

ActiveRecord has a solution to this, using has_many :through relationship.

Through associations allow for one model to be accessed through an intermediary association

Modify our model Article to look like this:

Now carry out the tasks by reloading the development environment The query will have an Inner join, the way it should be. We can even add another user (author) to the same article >> a = Article.find(1)>> a.users>> a.users << User.find(3) # this will insert one user for that article by adding one record in

#the user_articles table

Rails 2.0 Notes: Sunil Kelkar [email protected]

article.rb

class Article < ActiveRecord::Base

has_many :user_ articles has_many :users, :through => user_articles

end

Assignment:

1. Discuss the advantages of using option: through 2. Explore the web/books and discuss Polymorphic associations (I plan to add notes on this,

in the second week of our course)3. Read and Discuss about ActiveRecord ‘Transactions’(http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)

Page 22: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

As ActiveRecord is the biggest part of Rails 2.0 , I have tried to cover maximum details, I thought are good to start with. Let us turn our attention now to Data Definition part of ActiveRecord with Migrations which is our next chapter.

Rails 2.0 Notes: Sunil Kelkar [email protected]

TIPS: Overriding conventions.1. An alternate Table Name: If you don't want your table name to be plural, you can override it in your model class file using the ‘set_table_name’ method.

class User < ActiveRecord::Base     set_table_name "accounts"   # by convention it would be ‘users’ # alternatively we can say

# self.table_name = "accounts"end  

2. An alternate Primary Key

class User < ActiveRecord::Base     set_primary_key "userID"   # by convention it would be ‘id’   # but must be an Integer key # alternatively we can also say self.primary_key “userID”end

3. If we desire to ‘globally’ turn off the pluralization, open config/environment.rb file and at the end of the file, write

ActiveRecord::Base.pluralize_table_names = false  

Discuss: When you don’t want to follow any of the conventions, where are you saving time, or adding simplicity/maintainability to your code?

Read more: http://wiki.rubyonrails.com/rails/pages/HowToUseLegacySchemas

Page 23: Chapter 4 - ActiveRecord - The Model part of MVC

Chapter 4 – ActiveRecord

Rails 2.0 Notes: Sunil Kelkar [email protected]

Assignment :

Discuss : 1. Explore the concept of ‘SQL injection’ and how rails can protect itself from it, prove with an example.

2. Log files track every activity happening in AR., Is it worth it? What are the impacts when application goes live.

3. We know that the field level ‘validations’ are done at the client side (Using java script), then what’s the advantage of having ‘validations’ in models?

4. Explore ‘Finder’ methods further ( :conditions, :order, :group, :limit, :offset, :joins, :include, :select, :readonly etc) http://www.railsbrain.com/api/rails-2.0.2/doc/index.html?a=M001686&name=find

Workout:5. Explore conditional searching, check whether, extracting records based on search conditions, result in ‘case sensitive’ results?

A) Make use of following validations

1. validates_length_of :password,:minimum => 8 # more than 8 characters:maximum => 16 # no more than 16 characters:in => 8..16 # between 8 and 16 characters:too_short => ' password is too short':too_long => ‘ password is too long'

2. validates_confirmation_of :password 3. validates_acceptance_of :first_name

B) If all such validations are included, in what order are they carried out?

6. Write 2 custom methods to find out ‘next’ and ‘previous’ records in the model (record ‘id’ will be passed as an argument).

Note 1) Keep http://www.railsbrain.com or http:://www.noobkit.com handy 2) All validations take the extra options :message and :on 3) You would observe in rails, many methods take an “options” hash as a parameter, rather than having dozens of individual parameters

TIP: When available use the built-in classes and methods, rather than developing your own.