ActiveRecord

阅读更多
ActiveRecord 4新特性 http://www.oschina.net/translate/get-your-app-ready-for-rails-4?print

ActiveRecord 范围需要一个 Callable 对象。

在 Rails 4 中,所有 ActiveRecord 范围必须使用一个 callable 对象来定义:
#Rails 3.2
scope :recent, where(created_at: Time.now - 2.weeks)
#Rails 4
scope :recent, -> { where("created_at > ?", Time.now - 2.weeks).order("created_at desc") }
scope :active, -> { where(status: 'active') }

Activerecord-deprecated-finders 是 Rails 4.0 中默认提供废弃功能的依赖包,但它也将在 4.1 版本中被删除。因此你需要密切关注所有的警告信息,并开始修复这些警告。

Rails 指南 提供一个很有用的解释,关于在大多数情况下如何修改动态查找器:

    所有动态的方法除了 findby… 和 findby…! 外都已废弃,你可以使用如下的替代方法:

find_all_by_...改为 where(...).
find_last_by_...改为 where(...).last.
scoped_by_...改为 where(...).
find_or_initialize_by_...改为 where(...).first_or_initialize.
find_or_create_by_...改为 find_or_create_by(...) 或者 where(...).first_or_create.
find_or_create_by_...!改为 find_or_create_by!(...) 或者 where(...).first_or_create!.


#1 Caching with Instance Variables
@current_user ||= User.find(session[:user_id])


#2 Dynamic find_by Methods

@tasks = Task.find(:all, :conditions => ['complete = ?', false])
=>
@tasks = Task.find_all_by_complete(false)

@task = Task.find(:first, :conditions => ['complete =?', false], :order => 'created_at DESC')
=>
@task = Task.find_by_complete(false, :order => 'created_at DESC')


#3 Find Through Association

from
@tasks = Task.find(:all, :conditions => ['project_id = ? AND complete = ?', @project.id, false])

to (推荐使用,用association 进行关联)
@project = Project.find(params[:id])
@tasks = Task.find(:all, :conditions => ['project_id = ? AND complete = ?', @project.id, false])

 
#4 Move Find into Model
Move a find into the model to clean up the controllers and remove duplication. 
将 find 转移到model中,清洁controllers
from 

class TaskController < ApplicationController
  def index
    @tasks = Task.find_all_by_complete(:false, :order => "created_at DESC")
  end
end

to
class Task < ActiveRecord::Base
  belongs_to :project
  def self.find_incomplete
    find_all_by_complete(:false, :order => "created_at DESC")
  end
end


#5 Using with_scope

class Task < ActiveRecord::Base
  belongs_to :project

  def self.find_incomplete(options = {})
    with_scope :find => options do
      find_all_by_complete(false, :order => 'created_at DESC')
    end
  end
end
@tasks = @project.tasks.find_incomplete :limit => 20


#6 Shortcut Blocks with Symbol to_proc
projects.collect { |p| p.name } => ["Write another ASCIIcast", "Go out walking"]
projects.collect {&:name}       => ["Write another ASCIIcast", "Go out walking"]
projects.collect(&:name).collect(&:upcase)  => ["WRITE ANOTHER ASCIICAST", "GO OUT WALKING"]

projects.each {|project| project.save!}
==
projects.each(&:save!)


#16: Virtual Attributes (revised)
简介:对原生的attributes(collums),进行逻辑封装

schema.rb
create_table "products", :force => true do |t|
  t.string   "name"
  t.integer  "price_in_cents"
  t.datetime "released_at"
  ... ...
end

1、转换 {price_in_cents => price_in_dollars}


<%= f.label :price_in_dollars %>
<%= f.text_field :price_in_dollars %>

attr_accessible :name, :price_in_dollars
def price_in_dollars
  price_in_cents.to_d/100 if price_in_cents
end
def price_in_dollars=(dollars)
  self.price_in_cents = dollars.to_d*100 if dollars.present?
end

2、对 released_at 进行逻辑封装  {release_at => relese_at_text}

  attr_writer :released_at_text
  validate :check_released_at_text
  before_save :save_released_at_text
  def released_at_text
    @released_at_text || released_at.try(:strftime, "%Y-%m-%d %H:%M:%S")
  end
  
  def save_released_at_text
    self.released_at = Time.zone.parse(@released_at_text) if @released_at_text.present?
  end
  
  def check_released_at_text
    if @released_at_text.present? && Time.zone.parse(@released_at_text).nil?
      errors.add :released_at_text, "cannot be parsed"
    end
  rescue ArgumentError
    errors.add :released_at_text, "is out of range"
  end


3、支持创建新的 category
  <%= f.label :category_id %>
<%= f.collection_select :category_id, Category.order(:name), :id, :name %> or create one:<%= f.text_field :new_category %> attr_accessible :new_category attr_accessor :new_category before_save :create_category #在保存 product 前,先前所依赖的 category创建出来 def create_category self.category = Category.create!(name: new_category) if new_category.present? end

#17、habtm-checkboxs
典型的多对多的关系 ,且使用关联表Categorization
class Product < ActiveRecord::Base
  has_many :categorizations
  has_many :categories, through: :categorizations
end

class Categorization < ActiveRecord::Base
  belongs_to :category
  belongs_to :product
end

class Category < ActiveRecord::Base
  has_many :categorizations
  has_many :products, through: :categorizations
end

rails consle分析
p=produc.first 	=> select "products".* from "products" limit 1
p.category_ids	=> select categories.id from categories inner join categorizations on categories.id=categorization.category_id where categorizations.product_id=1
p.category_ids=[1,2] =>
1、select * from categories where id IN (1,2)
2、select * from categories inner join categorizations on categoris.category_id=categorization.category_id where categorizations.product_id=1
3、insert into categorizations ... values category_id=1,product_id=1
4、insert into categorizations ... values category_id=2,product_id=1
p.categories	=>[#,#]
	

_form.html.erb

  <%= hidden_field_tag "product[category_ids][]", nil %>
  <% Category.all.each do |category| %>
    <%= check_box_tag "product[category_ids][]", category.id, @product.category_ids.include?(category.id), id: dom_id(category) %>
    <%= label_tag dom_id(category), category.name %>
  <% end %>


#15 Fun with Find Conditions
# When searching for null values the correct syntax would be priority IS NULL, not priority = NULL
Task.find(:all, :conditions => ["completed = ? AND priority = ?", false, 2]) 
SELECT * FROM "tasks" WHERE (completed = 'f' AND priority = 2)


Task.find(:all, :conditions => ["complete=? and priority IS ?", false, nil])
SELECT * FROM "tasks" WHERE (completed = 'f' AND priority is NULL) 

Task.find(:all, :conditions => ["complete=? and priority IN (?)", false, [1,3]])
Task.find(:all, :conditions => ["complete=? and priority IN (?)", false, 1..3])

Task.find(:all, :conditions => { :complete => false, :priority => 1 })
Task.find(:all, :conditions => { :complete => false, :priority => nil })

Task.find(:all, :conditions => { :completed => false, priority => [1,3] } 
SELECT * FROM "tasks" WHERE (completed = 'f' AND priority IN (1,3)

Task.find(:all, :conditions => {:completed => false, priority => 2..4})
SELECT * FROM "tasks" WHERE ("tasks"."priority" BETWEEN 2 AND 4 AND "tasks"."completed" = 'f') 

Task.find_by_priority(1..5)
SELECT * FROM "tasks" WHERE ("tasks"."priority" BETWEEN 1 AND 5) LIMIT 1



#14 Performing Calculations on Models

Task.first.priority      => 3
  SELECT * FROM "tasks" LIMIT 1
  
Task.sum(:priority)     => 12 
  SELECT sum("tasks".priority) AS sum_priority FROM "tasks"
  
Task.sum(:priority, :conditions => {:completed => false }) => 2 
  SELECT sum("tasks".priority) AS sum_priority FROM "tasks" WHERE (completed="f")
  
Task.minimum(:priority)   => 2 
  SELECT min("tasks".priority) AS min_priority FROM "tasks" 
  
Task.maximum(:priority)   => 5 
  SELECT max("tasks".priority) AS max_priority FROM "tasks"
  
Task.average(:priority).to_f  => 3.0 
  SELECT avg("tasks".priority) AS avg_priority FROM "tasks"
  
  #Using the methods through associations.
project = Project.first => #   
project.tasks.sum(:priority, :conditions => {:completed => true})  => 10 
  
  SELECT sum("tasks".priority) AS sum_priority FROM "tasks" 
  WHERE ("tasks"."completed" = 't') AND ("tasks".project_id = 1)




#22 Eager Loading

<% @tasks.each do |task| %>
  
  • <%= link_to task.name, task %> in <%= task.project.name %>
  • <% end %> terminal Project Load (0.2ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60) CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60) CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60) CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60) CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60) 解决方案 1、eager loading reduce the calls to the database even further. We enable it by making a change in the TasksController. class TasksController < ApplicationController def index @tasks = Task.find(:all, :include => :project) end end 2、Including More Than One Association class Task < ActiveRecord::Base belongs_to :project has_many :comments end class TasksController < ApplicationController def index @tasks = Task.find(:all, :include => [:project, :comments]) end end 3、more complex associations. class Comment < ActiveRecord::Base belongs_to :task belongs_to :user end @tasks = Task.find(:all, :include => [:project, {:comments => :user }])


    #23 Counter Cache Column
    class Task < ActiveRecord::Base
      belongs_to :project, :counter_cache => true
      has_many :comments
    end
    
    migrations/006_add_tasks_count.rb
    def self.up
      add_column :projects, :tasks_count, :integer, :default => 0
      Project.reset_column_information
      Project.find(:all).each do |p|
        Project.update_counters p.id, :tasks_count => p.tasks.length
      end
    end
    def self.down
      remove_column :projects, :tasks_count
    end
    
    console
    p = Project.first
    => #
    >> p.tasks.create(:name => "New task")
    => #
    
    The project’s counter cache has been updated.


    #28 in_groups_of

    ruby teminal
    
      a = (1..12).to_a   => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
      a.in_groups_of(4)  => [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
      a.in_groups_of(3)  => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
      a.in_groups_of(5)  => [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, nil, nil, nil]]
      a.in_groups_of(5, false)  => [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
      a.in_groups_of(5, 0)  => [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 0, 0, 0]]
    
    rails
    
    
        <% @tasks.in_groups_of(4) do |tasks| %>
        
          <% tasks.each do |task| %>
          
          <% end %>
        
        <% end %>
    
    <%= task.name %>


    #29 group_by Month
    a = (1..20).to_a            => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
    a.group_by { |num| num/5 }  => {0=>[1, 2, 3, 4], 1=>[5, 6, 7, 8, 9], 2=>[10, 11, 12, 13, 14], 3=>[15, 16, 17, 18, 19], 4=>[20]}
    
    class TasksController < ApplicationController
      def index
        @tasks = Task.all
        @task_months = @tasks.group_by { |t| t.due_at.beginning_of_month }
      end
    end
    
    <% @task_months.keys.sort.each do |month| %>
    

    <%= month.strftime("%B %Y") %>

    <% for task in @task_months[month] %>

    <%= task.name %> due at <%= task.due_at.to_date.to_s(:long) %>

    <% end %> <% end %>

    你可能感兴趣的:(activerecord)