Rails 4資料庫操作大全

天贏金創發表於2014-04-24
腳手架的使用

生成腳手架
手動生成migration,然後每個欄位生成腳手架
rails generate scaffold Article title:string location:string excerpt:string body:text published_at:datetime --skip-migration

資料庫操作

1.生成新的記錄
①使用new建構函式

>> article = Article.new
=> #<Article id: nil, title: nil, body: nil, published_at: nil, created_at: nil,
updated_at: nil, excerpt: nil, location: nil>
>> article.new_record?
=> true
>> article.attributes
=> {"body"=>nil, "created_at"=>nil, "excerpt"=>nil, "location"=>nil,
"published_at"=>nil, "title"=>nil, "updated_at"=>nil}     

>> article.title = `RailsConf`
=> "RailsConf"

>> article.body = `RailsConf is the official gathering for Rails developers..`
=> "`RailsConf is the official gathering for Rails developers.."

>> article.published_at = `2013-04-13`
=> "2013-04-13"
>> article
=> #<Article id: nil, title: "RailsConf", body: "RailsConf is the official
gathering for Rails devel...", published_at: "2013-04-13 00:00:00",
created_at: nil, updated_at: nil, excerpt: nil, location: nil>
>> article.save
(0.1ms) begin transaction
SQL (2.2ms) INSERT INTO "articles" ("body", "created_at", "published_at",
"title", "updated_at") VALUES (?,?,?,?,?) [["body", "RailsConf is the official
gathering for Rails developers.."], ["created_at", Sat, 13 Apr 2013 15:50:29 UTC
+00:00], ["published_at", Wed, 13 Apr 2013 00:00:00 UTC +00:00], ["title",
"RailsConf"], ["updated_at", Sat, 13 Apr 2013 15:50:29 UTC +00:00]]
(2.9ms) commit transaction
=> true
>> Article.count
=> 1
>> article.new_record?
=> false


>> article = Article.new

>> article.title
= "Introduction to SQL"
>> article.body
= "SQL stands for Structured Query Language, .."
>> article.published_at
= Date.today

>> article.save


>> article = Article.new(:title => "Introduction to Active Record",
:body => "Active Record is Rails`s default ORM..", :published_at => Date.today)
>> article.save

②使用create方法

>> Article.create(:title => "RubyConf 2013", :body => "The annual RubyConf will
take place in..", :published_at => `2013-04-13`)
=> #<Article id: 4, title: "RubyConf 2013", body: "The annual RubyConf will take
place in..", published_at: "2013-04-13 00:00:00", created_at: "2013-04-13
23:17:19", updated_at: "2013-04-13 23:17:19", excerpt: nil, location: nil>
>> attributes = { :title => "Rails Pub Nite", :body
=> "Rails Pub Nite is every
3rd Monday of each month, except in December.", :published_at => "2013-04-13"}
=> {:title=>"Rails Pub Nite", :body=>"Rails Pub Nite is every
3rd Monday of each month, except in December.", :published_at=>" 2013-04-13"}
>> Article.create(attributes)

=> #<Article id: 5, title: "Rails Pub Nite", body: "Rails Pub Nite is every 3rd
Monday of each month, e...", published_at: "2013-04-13 00:00:00",
created_at: "2013-04-13 23:36:07", updated_at: "2013-04-13 23:36:07",
excerpt: nil, location: nil>     

>> Article.count
=> 5

③使用id查詢單條記錄

>> Article.find(3)
=> #<Article id: 3, title: "Introduction to Active Record", body: "Active Record
is Rails`s default ORM..", published_at: "2013-04-13 04:00:00",
created_at: "2013-04-13 23:15:37", updated_at: "2013-04-13 23:15:37",
excerpt: nil, location: nil>
>>article = Article.find(3)
=>#<Article id: 3 ...>
>>article.id
=>3
>>article.title
=>"Introduction to Active Record"

④使用first和last查詢單條記錄

>> Article.first
=> #<Article id: 1, title: "RailsConf", body: "RailsConf is the official
gathering for Rails devel...", published_at: "2013-04-13 00:00:00",
created_at: "2013-04-13 23:12:09", updated_at: "2010-04-13 23:12:09",
excerpt: nil, location: nil>
>> Article.last
=> #<Article id: 5, title: "Rails Pub Nite", body: "Rails Pub Nite is every 3rd
Monday of each month, e...", published_at: "2013-04-13 00:00:00",
created_at: "2013-04-13 23:36:07", updated_at: "2013-04-13 23:36:07",
excerpt: nil, location: nil>

⑤查詢多條記錄

>> articles = Article.all
=> [#<Article id: 1,..> #<Article id: 2,..>, #<Article id: 3,..>,
#<Article id: 4,..> , #<Article id: 5,..>]
>> articles.class
=> Array
>> articles.size
=> 5
>> articles[0]
=> #<Article id: 1, title: "RailsConf", body: "RailsConf is the official
gathering for Rails devel...", published_at: "2013-04-13 00:00:00",
created_at: "2013-04-13 23:12:09", updated_at: "2013-04-13 23:12:09",
excerpt: nil, location: nil>
>> articles[0].title
=> "RailsConf"
>> articles.first.title
=> "RailsConf"
>> articles.each { |article| puts article.title }
RailsConf
Introduction to SQL
Introduction to Active Record
RubyConf 2010
Rails Pub Nite
=> [#<Article id: 1,..> #<Article id: 2,..>, #<Article id: 3,..>,
#<Article id: 4,..> , #<Article id: 5,..>]

⑥使用order對資料進行排序
預設是降序,我們可以使用DESC改變為升序

>> articles = Article.order("published_at")
=> [#<Article id: 1,..> #<Article id: 2,..>, #<Article id: 3,..>,
#<Article id: 4,..> , #<Article id: 5,..>]
>> articles.each {|article| puts article.published_at }2013-04-13 00:00:00 UTC2013-04-13 04:00:00
UTC2013-04-13 04:00:00 UTC2013-04-13 00:00:00 UTC2013-04-13 00:00:00 UTC
=> [#<Article id: 1,..> #<Article id: 2,..>, #<Article id: 3,..>,
#<Article id: 4,..> , #<Article id: 5,..>]
>> articles = Article.order (`published_at DESC`)
=> [#<Article id: 4,..> #<Article id: 5,..>, #<Article id: 2,..>,
#<Article id: 3,..> , #<Article id: 1,..>]
>> articles.each {|article| puts article.published_at }2013-04-13 00:00:00 UTC2013-04-13 00:00:00
UTC2013-04-13 00:00:00 UTC2013-04-13 00:00:00 UTC2013-04-13 00:00:00 UTC
=> [#<Article id: 4,..> #<Article id: 5,..>, #<Article id: 2,..>,
#<Article id: 3,..> , #<Article id: 1,..>]

⑦條件查詢

>> Article.where(:title => `RailsConf`).first
=> #<Article id: 1, title: "RailsConf", body: "RailsConf is the official
gathering for Rails devel...", published_at: "2013-04-13 00:00:00",
created_at: "2013-04-13 23:12:09", updated_at: "2013-04-13 23:12:09",
excerpt: nil, location: nil>
>> Article.where(:title => `RailsConf`).all
=> [#<Article id: 1, title: "RailsConf", body: "RailsConf is the official
gathering for Rails devel...", published_at: "2013-04-13 00:00:00",
created_at: "2013-04-13 23:12:09", updated_at: "2013-04-13 23:12:09",
excerpt: nil, location: nil>]
>> Article.where(:title => `Unknown`).all
=> []

⑧更新記錄

>> article = Article.first
>> article.title = "Rails 4 is great"
>> article.published_at = Time.now
>> article.save
=> true

也可以一次性更新多個欄位

>> article = Article.first
>> article.update_attributes(:title => "RailsConf2013", :published_at => 1.day.ago)
=> true

或者更新一個欄位

>> article = Article.first
>> article.update_attribute(:title => "RailsConf2013")
=> true

⑨刪除記錄

 >> article = Article.last
    >> article.destroy
    => #<Article id: 5, title: "Rails Pub Nite", body: "Rails Pub Nite is every 3rd
    Monday of each month, e...", published_at: "2013-04-13 00:00:00",
    created_at: "2013-04-13 23:36:07", updated_at: "2013-04-13 23:36:07",
    excerpt: nil, location: nil>
    #或者在一行裡面操作
    >> Article.last.destroy

    >> Article.destroy(1)
    => [#<Article id: 1, title: "RailsConf", body: "RailsConf is the official
    gathering for Rails devel...", published_at: "2010-02-27 00:00:00",
    created_at: "2010-05-01 23:12:09", updated_at: "2010-05-01 23:12:09",
    excerpt: nil, location: nil>]

#刪除多條記錄
>> Article.destroy([2,3])
=> [#<Article id: 2, ..>, #<Article id: 3, ..>]

使用destroy是沒有返回值的,如果需要返回值,我們可以使用delete方法來刪除記錄

>> Article.delete(4)
=> 1

#資料庫中不存在5到6的記錄,所以返回0
>> Article.delete([5, 6])
=> 0

規定條件刪除相應的記錄

>> Article.delete_all("published_at < `2011-01-01`")
>> 0

1.在我們的模型中新增下面方法

class Article < ActiveRecord::Base
     validates_presence_of :title
     validates_presence_of :body

     def long_title
         "#{title} - #{published_at}"
     end
end

我們就使用上面的方法來運算元據庫了

#在命令列中操作模型
>> Article.create :title => `Advanced Active Record`, :published_at => Date.today,
:body => `Models need to relate to each other. In the real world, ...`
=> #<Article id: 6, title: "Advanced Active Record", ...>
>> Article.last.long_title
=> "Advanced Active Record - 2013-04-22 04:00:00 UTC"

2.資料庫之間的關係
①一對一關係
首先:我們建立兩張表
rails generate model User email:string password:string
rails generate model Profile user_id:integer name:string birthday:date bio:text color:string twitter:string
我們分別在兩個模型中建立對應的方法

#User模型
class User < ActiveRecord::Base
has_one :profile
end
#Profile模型
class Profile < ActiveRecord::Base
belongs_to :user
end

我們接下來可以在rails命令列中做測試了

>> user = User.create(:email => `user@example.com`, :password => `secret`)
=> #<User id: 1, email: "user@example.com", password: "secret",
created_at: "2013-04-02 15:10:07", updated_at: "2013-04-02 15:10:07">
>> profile = Profile.create(:name => `John Doe`,
:bio => `Ruby developer trying to learn Rails`)
=> #<Profile id: 1, user_id: nil, name: "John Doe", birthday: nil,
bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil,
created_at: "2013-04-02 15:10:55", updated_at: "2013-04-02 15:10:55"> 

>> user.profile
=> nil

>> user.profile = profile
=> #<Profile id: 1, user_id: 1, name: "John Doe", birthday: nil,
bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil,
created_at: "2013-04-02 15:10:55", updated_at: "2013-04-02 15:10:55">

>> user.profile
=> #<Profile id: 1, user_id: 1, name: "John Doe", birthday: nil,
bio: "Ruby developer trying to learn Rails", color: nil, twitter: nil,
created_at: "2013-04-02 15:10:55", updated_at: "2013-04-02 15:10:55">

>> user.create_profile :name => `Jane Doe`, :color => `pink`
=> #<Profile id: 2, user_id: 1, name: "Jane Doe", birthday: nil,
bio: nil, color: "pink", twitter: nil, created_at: "2013-04-02 15:18:57",
updated_at: "2013-04-02 15:18:57">

一對一模型中常用方法總結如下:
user.profile 返回user對應的profile物件
user.profile=(profile)對user的profile賦值
user.profile.nil? 返回user的profile是否為空,為空返回真
user.build_profile(attributes={}) 返回一條新的user的profile物件,但是不會儲存到資料庫,需要使用user.profile.save來儲存
user.create_profile(attributes={})返回user的profile物件,直接儲存到資料庫中

②一對多關係
我們使用user表對應article表,每個使用者都有很多的文章,但是每篇文章都只對應一個使用者
rails generate migration add_user_id_to_articles user_id:integer

#文章表模型
class Article < ActiveRecord::Base
    validates_presence_of :title
    validates_presence_of :body
    belongs_to :user#每篇文章指定屬於使用者
    def long_title
        "#{title} - #{published_at}"
    end
end
#使用者表   
class User < ActiveRecord::Base
    has_one :profile
    has_many :articles
end

我們在rails命令 行中使用示例

>> user = User.first
=> #<User id: 1, email: "user@example.com", password: "secret",
created_at: "2013-04-02 15:10:07", updated_at: "2013-04-02 15:10:07">
>> user.articles
=> []

>> user.articles << Article.first
=> [#<Article id: 6, ..., user_id: 1>]
>> user.articles.size
=> 1
>> user.articles
=> [#<Article id: 6, ..., user_id: 1>]

>> Article.first.user_id
=> 1

>> Article.first.user
=> #<User id: 1, email: "user@example.com", password: "secret",
created_at: "2013-04-02 15:10:07", updated_at: "2013-04-02 15:10:07">

一對多常用方法
user.articles 返回使用者模型中所有文章物件
user.articles=(articles) 替換使用者模型的所有文章物件,用articles來代替
user.articles << article 新增article物件到user的文章物件中
user.articles.delete(articles) 刪除文章模型中的一篇或者多篇文章
user.articles.empty? 判斷使用者例項的文章是否是空的
user.articles.size 返回使用者示例的文章數量
user.article_ids 返回使用者示例的文章id,以陣列形式返回
user.articles.clear 清空使用者的文章
user.articles.find 傳入文章的id,返回該文章
user.articles.build(attributes={}) 建立一個新的使用者文章物件,不會儲存,需要使用user.articles.last.save來儲存
PS:這種方法一般在實際的應用是這樣子使用的

product = Product.find(params[:product_id])
@line_item = @cart.line_items.build(product: prodcut)#將產品例項直接傳進來

respond_to do |format|
  if @line_item.save#使用它來儲存

user.articles.create(attributes={}) 直接建立文章物件,並且儲存到資料庫中。

③關係型資料庫擴充套件
i.定義預設排序

#指定單個的排序
class User < ActiveRecord::Base
    has_one :profile
    has_many :articles, -> { order(`published_at DESC`) }
end

#指定多個欄位排序
class User < ActiveRecord::Base
    has_one :profile
    has_many :articles, -> { order(`published_at DESC, title ASC`)}
end

ii.特殊依賴
當我們刪除一個使用者時,該使用者的所有文章都會被刪除

class User < ActiveRecord::Base
    has_one :profile
    has_many :articles, -> { order(`published_at DESC, title ASC`)}, :dependent => :destroy
end

當然我們也可以不刪除文章,可以設定被刪除使用者的所有文章的user_id為空

class User < ActiveRecord::Base
    has_one :profile
    has_many :articles, -> { order(`published_at DESC, title ASC`)}, :dependent => :nullify
end

④多對多關係
我們假定一篇文章有很多種分類,一個分類也有很多文章,我們使用rails generate model Category name:stringrails generate migration create_articles_categories分別建立分類表和中間表,中間表的定義如下

#The db/migrate/20130407002156_create_articles_categories.rb: File
class CreateArticlesCategories < ActiveRecord::Migration
    def change
        create_table :articles_categories, :id => false do |t|
        t.references :article
        t.references :category
        end
    end
    def self.down
        drop_table :articles_categories
    end
end

references方法和integer是一樣的效果,也就是說我們可以使用下面的方式來建立add_column :articles_categories, :article_id, :integer,add_column :articles_categories, :category_id, :integer
下面我們分別為Article模型和Category模型新增has_and_belongs_to_many方法

class Article < ActiveRecord::Base
    has_and_belongs_to_many :categories
end

class Category < ActiveRecord::Base
    has_and_belongs_to_many :articles
end

下面我們在rails命令列中測試,

>> article = Article.last
=> #<Article id: 8, title: "Associations", ...>
>> category = Category.find_by_name(`Programming`)
=> #<Category id: 1, name: "Programming", ..>
>> article.categories << category
=> [#<Category id: 1, name: "Programming", ..>]
>> article.categories.any?
=> true
>> article.categories.size
=> 1

>> category.articles.empty?
=> false
>> category.articles.size
=> 1
>> category.articles.first.title
>> "Associations"    

多對多關係需要注意,我們可以為每個欄位新增索引,增加資料庫的查詢速度,並且可以通過資料庫來保證每個欄位的唯一性

add_index :articles_categories, :article_id
add_index :articles_categories, :category_id
add_index :articles_categories, [:article_id, :category_id], unique: true
自定義方法來模擬多對多關係

還是通過文章和分類模型來定義多對多關係
1.建立ac資料庫

class CreateAcs < ActiveRecord::Migration
  def change
    create_table :acs do |t|
      t.integer :article_id
      t.integer :category_id

      t.timestamps
    end
  end
end

2.分別在文章和分類模型中定義下面的方法
文章模型

class Article < ActiveRecord::Base
    validates_presence_of :title
    validates_presence_of :body

    belongs_to :user
    has_many :acs, dependent: :destroy #這裡我們不需要制定外來鍵,預設是article_id
    def long_title
        "#{title} -- #{published_at}"       
    end

    def allc
        categories = []
        self.acs.each {|i| categories << Category.find(i.category_id)}
        return categories
    end 
end

分類模型

class Category < ActiveRecord::Base
    has_many :acs, dependent: :destroy#這裡我們不需要制定外來鍵,預設是category_id

    def alla
        articles = []
        self.acs.each {|i| articles << Article.find(i.article_id)}
        return articles
    end
end

最後在ac類中定義下面的方法

class Ac < ActiveRecord::Base
    belongs_to :article, class_name: "Article"
  belongs_to :category, class_name: "Category"
end

這樣我們就可以模擬多對多關係了,應該比預設的方法查詢速度要快很多吧!

使用where方式
>> Aricle.where(:title => `Advanced Active Record`)
=> [#<Article id: 6, title: "Advanced Active Record", ...>]
使用原生SQL語句
>> Article.where("created_at > `23-04-2013` OR body NOT LIKE `%model%`")
=> [#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]


>> Article.where("created_at > `23-04-2013` AND body NOT LIKE `%model%`")
=> []
使用陣列條件語義
>> Article.where("published_at < ?", Time.now)
=> [#<Article id: 6, title: "Advanced Active Record", ...>]

>> Article.where("published_at < ?", Time.now).to_sql
=> "SELECT
"articles".* FROM
"articles"
WHERE
(published_at < `2013-04-02 16:27:51.059277`)"

>> Article.where("created_at = ?", Article.last.created_at)
=> [#<Article id: 8, title: "Associations", ...>]

>> Article.where("created_at = ? OR body LIKE ?", Article.last.created_at, `model`)
=> [#<Article id: 8, title: "Associations", ...>]

>> Article.where("title LIKE :search OR body LIKE :search",
{:search => `%association%`})
=> [#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]
使用代理模式
>> User.first.articles.all
=> [#<Article id: 8, title: "Associations", ...>] 

current_user.articles.create(:title => `Private`, :body => ‘Body here..’)
其他一些常用方法
>> Article.all
=> [#<Article id: 6, title: "Advanced Active Record", ...>,
#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]
>> Article.order("title ASC")
=> [#<Article id: 6, title: "Advanced Active Record", ...>,
#<Article id: 8, title: "Associations", ...>,
#<Article id: 7, title: "One-to-many associations", ...>]
>> Article.limit(1)
=> [#<Article id: 6, title: "Advanced Active Record", ...>]
>> Article.order("title DESC").limit(2)
=> [#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]    
>> Article.all
=> [#<Article id: 6, title: "Advanced Active Record", ...>,
#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]
>> Article.order("title ASC")
=> [#<Article id: 6, title: "Advanced Active Record", ...>,
#<Article id: 8, title: "Associations", ...>,
#<Article id: 7, title: "One-to-many associations", ...>]
>> Article.limit(1)
=> [#<Article id: 6, title: "Advanced Active Record", ...>]
>> Article.order("title DESC").limit(2)
=> [#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]
預設作用域

首先我們看看資料庫的預設排序方法

>> Category.all
=> [#<Category id: 1, name: "Programming", ...>, #<Category id: 2, name: "Event", ...>,
#<Category id: 3, name: "Travel", ...>, #<Category id: 4, name: "Music", ..>,
#<Category id: 5, name: "TV", ...>]

在category模型中我們新增default_scope方法,以後的查詢結構預設會以分類名排序。

class Category < ActiveRecord::Base
    has_and_belongs_to_many :articles 
    default_scope lambda { order(`categories.name`) }
end

我們在rails命令列中進行測試

>> reload!
Reloading...
>> Category.all
=> [#<Category id: 2, name: "Event", ...>, #<Category id: 4, name: "Music", ...>,
#<Category id: 1, name: "Programming", ...>, #<Category id: 5, name: "TV", ...>,
#<Category id: 3, name: "Travel", ...>]
自定義作用域

首先我們需要在模型中定義方法,如下

class Article < ActiveRecord::Base
    scope :published, lambda { where("articles.published_at IS NOT NULL") }
    scope :draft, lambda { where("articles.published_at IS NULL") }
    scope :recent, lambda { published.where("articles.published_at > ?",
    1.week.ago.to_date)}
    scope :where_title, lambda { |term| where("articles.title LIKE ?", "%#{term}%") }

    def long_title
        "#{title} - #{published_at}"
    end
end

我們在命令列中測試

>> Article.published
=> [#<Article id: 6, title: "Advanced Active Record", ...>]
>> Article.draft
=> [#<Article id: 7, title: "One-to-many associations", ...>,
#<Article id: 8, title: "Associations", ...>]
>> Article.recent
=> [#<Article id: 6, title: "Advanced Active Record", ...>]
>> Article.draft.where_title("one")
=> [#<Article id: 7, title: "One-to-many associations", ...>]
>> Article.where_title("Active")
=> [#<Article id: 6, title: "Advanced Active Record", ...>]

相關文章