Rails 3: Difference between revisions

From Wiki
Jump to navigation Jump to search
Line 202: Line 202:


=== Associations ===
=== Associations ===
==== One-to-one ====
<source lang="ruby">
class User < ActiveRecord::Base
    has_one :profile          # assumes User.profile_id column
end                            # creates User.profile method
-------------------------------
class Profile < ActiveRecord::Base
    belongs_to :user          # assumes Profile.user_id column
end                            # creates Profile.user method
</source>
==== One-to-many ====
<source lang="ruby">
<source lang="ruby">
class Message < ActiveRecord::Base
class Message < ActiveRecord::Base
     has_many :attachments
     has_many :attachments # creates Message.attachments method
end
end
</source>
-------------------------------
<source lang="ruby">
class Attachment < ActiveRecord::Base
class Attachment < ActiveRecord::Base
     belongs_to :message  
     belongs_to :message   # assumes Attachment.message_id column
end
end
</source>
</source>

Revision as of 20:37, 28 June 2011

Application Setup

  • create project with
rails new my_project
rails -d mysql new my_project  # if you don't want the default sqlite3
  • add gem dependencies to Gemfile at the root level
  • update config/application.rb to load needed dependencies and update defaults
  • double-check /config/initializers and /config/environments
  • edit config/database.yml to connect to your database

Notes

Files in lib/ are not automatically loaded, so you need to require them.

In config/environments/development.rb, set

config.action_mailer.perform_deliveries = false

No delivery attempt is performed, but you can still see the mail in the log file to check it looks good

Database Setup

  • create database with
rake db:create

Create a table and its controller

rails generate model Article

Edit db/migrate/20100223220648_create_articles.rb to look like this:

class CreateArticles < ActiveRecord::Migration
    def self.up 
        create_table :articles do |t|
            t.string :title
            t.text :body
            t.datetime :published_at
            t.timestamps
        end
    end
    def self.down
        drop_table :articles
    end
end

To create/update the table:

rake db:migrate

To generate the controller:

rails generate controller articles

Make a scaffold

To make a scaffold for the table to allow easy create/update/delete in development:

rails generate scaffold Article title:string body:text published_at:datetime --skip-migration

WARNING: As you develop your application, you will eventually replace most or even all of the code that the scaffold generates in the controller.

Update table and scaffold

To modify the table, generate a custom migration:

rails generate migration add_excerpt_and_location_to_articles excerpt:string location:string

creates this migration file db/migrate/20100223232337_add_excerpt_and_location_to_articles.rb:

class AddExcerptAndLocationToArticles < ActiveRecord::Migration
    def self.up
        add_column :articles, :excerpt, :string
        add_column :articles, :location, :string
    end
    def self.down
        remove_column :articles,
        :excerpt remove_column :articles, :location
    end
end

Run rake db:migrate again to update the database.

Now update the scaffold:

rails generate scaffold Article title:string location:string excerpt:string body:text published_at:datetime --skip-migration

Add validations to the model

Edit app/models/article.rb:

class Article < ActiveRecord::Base
    validates :title, :presence => true
    validates :body, :presence => true
end

Other rails commands

rails console
rails dbconsole
rails server
rails runner

Routing

Configured in config/routes.rb

match 'products/:id' => 'products#show'

The url http://localhost:3000/products/8 will be mapped to the show action of the products controller with params[:id] set to 8

To create a link to this route (old way):

link_to "Products", :controller => "products", :action => "show", :id => 1

To restrict the HTTP method, use get or post instead of match:

get 'products/:id' => 'products#show'

To redirect:

match "/foo", :to => redirect("/bar")

Logging

Use these in models, views, controllers to send timestamped messages to the log.

logger.debug "debug message"
logger.info "info message"
logger.warn "something bad"
logger.error "something broke"
logger.fatal "application dead"

Active Record

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

Associations

One-to-one

class User < ActiveRecord::Base
    has_one :profile           # assumes User.profile_id column
end                            # creates User.profile method
-------------------------------
class Profile < ActiveRecord::Base 
    belongs_to :user           # assumes Profile.user_id column
end                            # creates Profile.user method


One-to-many

class Message < ActiveRecord::Base
    has_many :attachments  # creates Message.attachments method
end
-------------------------------
class Attachment < ActiveRecord::Base
    belongs_to :message   # assumes Attachment.message_id column
end