Rails 3 Model: Difference between revisions
Jump to navigation
Jump to search
Line 155: | Line 155: | ||
has_many :articles, :dependent => :destroy # destroy article when user is destroyed, also ":delete" and ":nullify" | has_many :articles, :dependent => :destroy # destroy article when user is destroyed, also ":delete" and ":nullify" | ||
</source> | </source> | ||
==== has_many :through ==== | |||
<source lang="ruby"> | |||
class User < ActiveRecord::Base | |||
has_one :profile | |||
has_many :articles, :order => 'published_at DESC, title ASC', :dependent => :nullify | |||
has_many :replies, :through => :articles, :source => :comments | |||
end | |||
</source> | |||
<code>user.articles.comments</code> becomes shortened to <code>user.replies</code> | |||
=== Many-to-many === | === Many-to-many === |
Revision as of 18:07, 30 June 2011
Model Definition
- Generate a model to create model objects and underlying tables:
rails generate model Category name:string
This creates both a model definition file in app/models
and a database migration file in db/migrate
- Generate a migration when you just want to modify the database:
rails generate migration create_articles_categories # create join table
This makes a new database migration script in db/migrate
. Edit this as necessary before running the migration.
- To run all outstanding migrations:
rake db:migrate
- To roll back to a particular timestamp:
rake db:migrate VERSION=20090124223305
(see the schema_migrations
table in your database for a definitive list of timestamps)
Migrations
Create table options
- column definitions
t.column(:name, :string) # verbose style
t.string :name # shorter style
- column types:
binary (aka blob), boolean, date, datetime, decimal, float, integer, string, text, time, timestamp decimal has :precision (total number of digits) and :scale (number of digits after decimal place)
- column options:
:default => value
:limit => size
:null => false
- change column definition:
t.change(:name, :string, :limit => 80)
- rename column:
t.rename(:description, :name)
- foreign key:
create_table :accounts do
t.belongs_to(:person)
end
- add Active Record-maintained timestamp (
created_at
andupdated_at
) columns to the table.
t.timestamps
- make an index:
t.index(:name) # a simple index
t.index([:branch_id, :party_id], :unique => true) # a unique index
- To create a join table with no default
id
primary key:
create_table :ingredients_recipes, :id => false do |t|
t.column :ingredient_id, :integer
t.column :recipe_id, :integer
end
Seed Data
A default part of the rails app is the file db/seeds.rb
. Use this to seed the database with test data:
user = User.create :email => 'mary@example.com', :password => 'guessit'
Category.create [{:name => 'Programming'}, {:name => 'Event'}, {:name => 'Travel'}, {:name => 'Music'}, {:name => 'TV'}]
Then load the seed data:
rake db:seed # just populate with seed data, can introduce duplicates rake db:setup # recreates the database and populates with seed data
Associations
One-to-one
class User < ActiveRecord::Base
has_one :profile # creates User.profile method
end
-------------------------------
class Profile < ActiveRecord::Base
belongs_to :user # assumes Profile.user_id column
end # creates Profile.user method
REMINDER: the belongs_to
declaration always goes in the class with the foreign key
has_one automatic methods
user.profile #=> #<Profile id: 2, user_id: 1, ...>
user.profile.nil? #=> false
user.build_profile(:bio => 'eats leaves') #=> #<Profile id: nil, user_id: 1, ...> # not automatically saved
user.create_profile(:bio => 'eats leaves') #=> #<Profile id: 3, user_id: 1, ...> # automatically saved
has_one options
has_one :profile, :class_name => 'Account' # refer to user.account instead of user.profile
has_one :profile, :foreign_key => 'account_id' # refer to user.account instead of user.profile
has_one :profile, :conditions => "active = 1" # only specific profiles are considered
has_one :profile, :dependent => :destroy # call destroy on profile when user is destroyed, also ":delete" and ":nullify"
One-to-many
class User < ActiveRecord::Base
has_many :articles # creates User.articles method
end
-------------------------------
class Article < ActiveRecord::Base
belongs_to :user # assumes Article.user_id column
end # creates Article.user method
has_many automatic methods
user.articles.size # array length
user.article_ids # list of ids
user.articles << Article.first # automatically saves association in Article.user_id
user.articles.delete(articles) # remove articles by setting Article.user_id to null
user.articles.clear # remove all articles by setting Article.user_id to null
user.articles.find(conditions) # find a subset of user.articles
user.articles.build(:title => 'Ruby 1.9') # return associated article but don't save yet
user.articles.create(:title => 'Ruby 1.9') # return associated article that has already been saved
has_many options
has_many :articles, :class_name => 'Post' # refer to user.posts instead of user.articles
has_many :articles, :foreign_key => 'post_id' # refer to user.posts instead of user.articles
has_many :articles, :conditions => "active = 1" # only consider specific articles
has_many :articles, :order => "published_at DESC" # default order of user.articles
has_many :articles, :dependent => :destroy # destroy article when user is destroyed, also ":delete" and ":nullify"
has_many :through
class User < ActiveRecord::Base
has_one :profile
has_many :articles, :order => 'published_at DESC, title ASC', :dependent => :nullify
has_many :replies, :through => :articles, :source => :comments
end
user.articles.comments
becomes shortened to user.replies
Many-to-many
This requires a join table:
rails generate migration create_articles_categories
simple join
class Article < ActiveRecord::Base
validates :title, :presence => true
validates :body, :presence => true
belongs_to :user
has_and_belongs_to_many :categories
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :articles
end
rich join
Create a model in addition to the join table. This allows the join itself to have other properties.
Model Manipulation
Create and Save
article = Article.new # make an empty object
article.title = "My Title" # add attributes
article.author = "Herman"
article.save # save to database
article.create(:title => "My Title", :author => "Herman") # make, set attributes, and save
article.new_record? # new means not saved to database
Find
Article.find(3) # look for id = 3
Article.first # same as Article.find(:first), uses "LIMIT 1" in SQL, so may not be id = 1
Article.last # same as Article.find(:last)
Article.all # same as Article.find(:all)
Article.where(:title => 'RailsConf').first # chaining
Article.find_by_title('RailsConf') # dynamic finder
Update
article = Article.first
article.title = "Rails 3 is great"
article.published_at = Time.now
article.save
article = Article.first
article.update_attributes(:title => "RailsConf2010", :published_at => 1.day.ago) # saves too
Delete
Article.find(3).destroy # find, then destroy
Article.destroy(3) # same thing
Article.delete(3) # delete directly from database with no object callbacks
Article.delete_all("published_at < '2011-01-01'") # conditional
Validation and Errors
class Article< ActiveRecord::Base
validates :title, :presence => true
validates :body, :presence => true
end
article = Article.new
article.errors.any? # false
article.save # returns false because of validation
article.errors.full_messages # ["Title can't be blank", "Body can't be blank"]
article.errors.on(:title) # "can't be blank"
article.valid? # false