Rails Modelcheatsheet
贡献者:BAI
创建模型
创建
RUBYrails g model User
使用模型
查询方法
RUBYitems = Model .where(first_name: 'Harvey') .where('id = 3') .where('id = ?', 3)
ORDER(.order(:title) .order(title: :desc) .order("title DESC")
REORDER(.reorder(:title) # discards other .order's .rewhere(...) # discards other .where's
LIMIT(2).limit(2) .offset(1) .uniq
详见: QueryMethods
进阶查询方法
RUBYitems = Model .select(:id) .select([:id, :name])
GROUP(.group(:name) # GROUP BY name .group('name AS grouped_name, age') .having('SUM(price) > 30') # 需要再 .group 后执行 having
INCLUDES(.includes(:user) .includes(user: [:articles])
REFERENCES(.references(:posts) # 同: .where("posts.name = 'foo'").references(:posts)
Find 方法
RUBYitem = Model.find(id)item = Model.find_by_email(email)item = Model.where(email: email).first
EXISTS?(5)Model .exists?(5) .exists?(name: "David")
FIRST.first .last .find_nth(4, [offset])
详见: FinderMethods
保存
RUBYitem.new_record?item.persisted?item.destroyed?item.serialize_hash
RUBYitem.saveitem.save! # 功能同上,但抛出异常
RUBYitem.update name: 'John'item.update! name: 'John'
RUBYitem.update_column, name: 'John' # update_column 会跳过 validation 和 callbacksitem.update_columns name: 'John'item.update_columns! name: 'John'
RUBYitem.touch # 更新 :updated_atitem.touch :published_at
RUBYitem.destroyitem.delete # 跳过 callback
RUBYModel.create # 等同于先 #new 再 #saveModel.create! # 功能同上,但抛出异常
详见: Persistence
属性赋值
RUBYitem.attributes # #<Hash>
RUBYitem.attributes = { name: 'John' } # 合并 hash 中相应的值,但不保存item.assign_attributes name: 'John' # 同上
Dirty
RUBYitem.changed?item.changed # ['name']item.changed_attributes # { 'name' => 'Bob' } - original valuesitem.changes # { 'name' => ['Bob', 'Robert'] }item.previous_changes # #save 之后可调用item.restore_attributes
RUBYitem.name = 'Robert'item.name_was # 'Bob'item.name_change # [ 'Bob', 'Robert' ]item.name_changed? # trueitem.name_changed?(from: 'Bob', to: 'Robert')
详见: Dirty
验证(Validation)
RUBYitem.valid?item.invalid?
详见: Validations
计算
RUBYPerson.countPerson.count(:age)
RUBYPerson.average(:age)Person.maximum(:age)Person.minimum(:age)Person.sum('2 * age')
RUBYPerson.calculate(:count, :all)
进阶用法:
RUBYPerson.distinct.countPerson.group(:city).count
详见: Calculations
动态属性名称的 Find 方法
不太建议这么使用
RUBY# 返回一条记录Person.find_by_name(name)Person.find_last_by_name(name)Person.find_or_create_by_name(name)Person.find_or_initialize_by_name(name)
RUBY# 返回一组记录Person.find_all_by_name(name)
RUBY# 抛出异常Person.find_by_name!(name)
RUBY# 也可以用 `scoped` 代替 `find`Person.scoped_by_user_name
关联(Association)
关联
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
Has many
RUBYbelongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'has_many :comments, -> { order('posted_on DESC') }has_many :comments, :include => :authorhas_many :people, :class_name => "Person"has_many :people, :conditions => "deleted = 0"has_many :tracks, -> { order(:position) }has_many :comments, :dependent => :nullifyhas_many :comments, :dependent => :destroyhas_many :tags, :as => :taggablehas_many :reports, :readonly => truehas_many :subscribers, :through => :subscriptions, class_name: "User", :source => :userhas_many :subscribers, :finder_sql => 'SELECT DISTINCT people.* ' + 'FROM people p, post_subscriptions ps ' + 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' + 'ORDER BY p.first_name'
belongs to
RUBYbelongs_to :author, :dependent => :destroy # 或 :delete :class_name => "Person" :select => "*" :counter_cache => true :counter_cache => :custom_counter :include => "Book" :readonly => true :conditions => 'published = true' :touch => true :touch => :authors_last_updated_at :primary_key => "name" :foreign_key => "author_name"
Many-to-many
RUBYclass Programmer < ActiveRecord::Base has_many :assignments has_many :projects, :through => :assignmentsend
RUBYclass Project < ActiveRecord::Base has_many :assignments has_many :programmers, :through => :assignmentsend
RUBYclass Assignment belongs_to :project belongs_to :programmerend
Many-to-many (HABTM)
RUBYhas_and_belongs_to_many :projectshas_and_belongs_to_many :projects, :include => [ :milestones, :manager ]has_and_belongs_to_many :nations, :class_name => "Country"has_and_belongs_to_many :categories, :join_table => "prods_cats"has_and_belongs_to_many :categories, :readonly => truehas_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>"DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}"
Polymorphic 关联
RUBYclass Post has_many :attachments, as: :parentend
RUBYclass Image belongs_to :parent, polymorphic: trueend
RUBYcreate_table :images do |t| t.references :post, polymorphic: trueend
验证(Validation)
验证
RUBYclass Person < ActiveRecord::Base
RUBY# 存在 validates :name, presence: true
RUBY# 是否接受 validates :terms, acceptance: true
RUBY# 确认 validates :email, confirmation: true
RUBY# 唯一 validates :slug, uniqueness: true validates :slug, uniqueness: { case_sensitive: false } validates :holiday, uniqueness: { scope: :year, message: 'yearly only' }
RUBY# 格式 validates :code, format: /regex/ validates :code, format: { with: /regex/ }
RUBY# 长度 validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: => 6..20 } validates :number, length: { is: => 6 }
RUBY# 是否包含 validates :gender, inclusion: %w(male female) validates :gender, inclusion: { in: %w(male female) } validates :lol, exclusion: %w(xyz)
RUBY# 数字 validates :points, numericality: true validates :played, numericality: { only_integer: true } # ... greater_than, greater_than_or_equal_to, # ... less_than, less_than_or_equal_to # ... odd, even, equal_to
RUBY# 验证关系是否有效 has_many :books validates_associated :books
RUBY# 长度 validates :content, length: { minimum: 300, maximum: 400, tokenizer: lambda { |str| str.scan(/\w+/) }, too_short: "must have at least %{count} words", too_long: "must have at most %{count} words" }
RUBY# 多属性 validates :login, :email, presence: true
RUBY# 条件 validates :description, presence: true, if: :published? validates :description, presence: true, if: lambda { |obj| .. }
RUBYvalidates :title, presence: true, on: :save # :save | :create | :update
RUBYend
自定义验证
RUBYclass Person < ActiveRecord::Base validate :foo_cant_be_nil def foo_cant_be_nil errors.add(:foo, 'cant be nil') if foo.nil? endend
错误
RUBYrecord.errors.valid? # → falserecord.errors # → { :name => ["can't be blank"] }record.errors.messages # → { :name => ["can't be blank"] }
RUBYrecord.errors[:name].any?
其他 API
Callback
大量更新
RUBYPerson.update 15, name: "John", age: 24Person.update [1,2], [{name: "John"}, {name: "foo"}]
Join
RUBY# 基础 JoinStudent.joins(:schools).where(schools: { type: 'public' })Student.joins(:schools).where('schools.type' => 'public' )
RUBY# 多关系Article.joins(:category, :comments)
RUBY# 嵌套关系Article.joins(comments: :guest)
RUBY# SQLAuthor.joins( 'INNER JOIN posts ' + 'ON posts.author_id = authors.id ' + 'AND posts.published = "t"')
Where 语句
RUBYwhere('name = ?', 'John')where(['name = :name', { name: 'John' }])
序列化
RUBYclass User < ActiveRecord::Base serialize :preferencesend
RUBYuser = User.create( preferences: { 'background' => 'black', 'display' => 'large' })
RUBY# 只接收 Hash 参数class User < ActiveRecord::Base serialize :preferences, Hashend
RUBY# 如果类型不匹配会抛出 SerializationTypeMismatch 异常user = User.create(preferences: %w(one two three))User.find(user.id).preferences
其他技巧
覆写存取器(accessor)
RUBYclass Song < ActiveRecord::Base def length=(minutes) write_attribute(:length, minutes.to_i * 60) end def length read_attribute(:length) / 60 endend
详见: http://api.rubyonrails.org/classes/ActiveRecord/Base.html
Callback
- after_create
- after_initialize
- after_validation
- after_save
- after_commit