325 lines
7.5 KiB
HTML
325 lines
7.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title></title>
|
||
<link href="style.css" rel="stylesheet" />
|
||
</head>
|
||
<body>
|
||
<h1>Rails Models</h1>
|
||
<h3>Generating models</h3>
|
||
|
||
<pre><code>$ rails g model User
|
||
</code></pre>
|
||
|
||
<h3>Associations</h3>
|
||
|
||
<pre><code>belongs_to
|
||
has_one
|
||
has_many
|
||
has_many :through
|
||
has_one :through
|
||
has_and_belongs_to_many
|
||
|
||
belongs_to :author,
|
||
class_name: 'User',
|
||
dependent: :destroy // delete this
|
||
</code></pre>
|
||
|
||
<h3>Has many</h3>
|
||
|
||
<pre><code>belongs_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"
|
||
has_many :comments, :include => :author
|
||
has_many :people, :class_name => "Person"
|
||
has_many :people, :conditions => "deleted = 0"
|
||
has_many :tracks, :order => "position"
|
||
has_many :comments, :dependent => :nullify
|
||
has_many :comments, :dependent => :destroy
|
||
has_many :tags, :as => :taggable
|
||
has_many :reports, :readonly => true
|
||
has_many :subscribers, :through => :subscriptions, class_name: "User", :source => :user
|
||
has_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'
|
||
</code></pre>
|
||
|
||
<h3>Many-to-many</h3>
|
||
|
||
<p>If you have a join model:</p>
|
||
|
||
<pre><code>class Programmer < ActiveRecord::Base
|
||
has_many :assignments
|
||
has_many :projects, :through => :assignments
|
||
end
|
||
|
||
class Project < ActiveRecord::Base
|
||
has_many :assignments
|
||
has_many :programmers, :through => :assignments
|
||
end
|
||
|
||
class Assignment
|
||
belongs_to :project
|
||
belongs_to :programmer
|
||
end
|
||
</code></pre>
|
||
|
||
<p>Or HABTM:</p>
|
||
|
||
<pre><code>has_and_belongs_to_many :projects
|
||
has_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 => true
|
||
has_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}"
|
||
</code></pre>
|
||
|
||
<h3>Polymorphic associations</h3>
|
||
|
||
<pre><code>class Post
|
||
has_many :attachments, :as => :parent
|
||
end
|
||
|
||
class Image
|
||
belongs_to :parent, :polymorphic => true
|
||
end
|
||
</code></pre>
|
||
|
||
<p>And in migrations:</p>
|
||
|
||
<pre><code>create_table :images do
|
||
t.references :post, :polymorphic => true
|
||
end
|
||
</code></pre>
|
||
|
||
<h2>Migrations</h2>
|
||
|
||
<h3>Run migrations</h3>
|
||
|
||
<pre><code>$ rake db:migrate
|
||
</code></pre>
|
||
|
||
<h3>Migrations</h3>
|
||
|
||
<pre><code>create_table :users do |t|
|
||
t.string :name
|
||
t.text :description
|
||
|
||
t.primary_key :id
|
||
t.string
|
||
t.text
|
||
t.integer
|
||
t.float
|
||
t.decimal
|
||
t.datetime
|
||
t.timestamp
|
||
t.time
|
||
t.date
|
||
t.binary
|
||
t.boolean
|
||
end
|
||
|
||
options:
|
||
:null (boolean)
|
||
:limit (integer)
|
||
:default
|
||
:precision (integer)
|
||
:scale (integer)
|
||
</code></pre>
|
||
|
||
<h3>Tasks</h3>
|
||
|
||
<pre><code>create_table
|
||
change_table
|
||
drop_table
|
||
add_column
|
||
change_column
|
||
rename_column
|
||
remove_column
|
||
add_index
|
||
remove_index
|
||
</code></pre>
|
||
|
||
<h3>Associations</h3>
|
||
|
||
<pre><code>t.references :category # kinda same as t.integer :category_id
|
||
|
||
# Can have different types
|
||
t.references :category, polymorphic: true
|
||
</code></pre>
|
||
|
||
<h3>Add/remove columns</h3>
|
||
|
||
<pre><code>$ rails generate migration RemovePartNumberFromProducts part_number:string
|
||
|
||
class RemovePartNumberFromProducts < ActiveRecord::Migration
|
||
def up
|
||
remove_column :products, :part_number
|
||
end
|
||
|
||
def down
|
||
add_column :products, :part_number, :string
|
||
end
|
||
end
|
||
</code></pre>
|
||
|
||
<h2>Validation</h2>
|
||
|
||
<pre><code>class Person < ActiveRecord::Base
|
||
|
||
# Checkboxes
|
||
validates :terms_of_service, :acceptance => true
|
||
|
||
# Validate associated records
|
||
has_many :books
|
||
validates_associated :books
|
||
|
||
# Confirmation (like passwords)
|
||
validates :email, :confirmation => true
|
||
|
||
# Format
|
||
validates :legacy_code, :format => {
|
||
:with => /\A[a-zA-Z]+\z/,
|
||
:message => "Only letters allowed"
|
||
}
|
||
|
||
# Length
|
||
validates :name, :length => { :minimum => 2 }
|
||
validates :bio, :length => { :maximum => 500 }
|
||
validates :password, :length => { :in => 6..20 }
|
||
validates :number, :length => { :is => 6 }
|
||
|
||
# Length (full enchalada)
|
||
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"
|
||
}
|
||
end
|
||
|
||
# Numeric
|
||
validates :points, :numericality => true
|
||
validates :games_played, :numericality => { :only_integer => true }
|
||
|
||
# Non empty
|
||
validates :name, :presence => true
|
||
|
||
# Multiple
|
||
validate :login, :email, :presence => true
|
||
end
|
||
</code></pre>
|
||
|
||
<h3>Custom validations</h3>
|
||
|
||
<pre><code>class Person < ActiveRecord::Base
|
||
validate :foo_cant_be_nil
|
||
|
||
def foo_cant_be_nil
|
||
errors.add(:foo, 'cant be nil') if foo.nil?
|
||
end
|
||
end
|
||
</code></pre>
|
||
|
||
<h2>API</h2>
|
||
|
||
<pre><code>items = Model.find_by_email(email)
|
||
items = Model.where(first_name: "Harvey")
|
||
|
||
item = Model.find(id)
|
||
|
||
item.serialize_hash
|
||
item.new_record?
|
||
|
||
item.create # Same an #new then #save
|
||
item.create! # Same as above, but raises an Exception
|
||
|
||
item.save
|
||
item.save! # Same as above, but raises an Exception
|
||
|
||
item.update
|
||
item.update_attributes
|
||
item.update_attributes!
|
||
|
||
item.valid?
|
||
item.invalid?
|
||
</code></pre>
|
||
|
||
<p>http://guides.rubyonrails.org/active<em>record</em>validations_callbacks.html</p>
|
||
|
||
<h3>Mass updates</h3>
|
||
|
||
<pre><code># Updates person id 15
|
||
Person.update 15, name: "John", age: 24
|
||
Person.update [1,2], [{name: "John"}, {name: "foo"}]
|
||
</code></pre>
|
||
|
||
<h3>Joining</h3>
|
||
|
||
<pre><code>Student.joins(:schools).where(:schools => { :type => 'public' })
|
||
Student.joins(:schools).where('schools.type' => 'public' )
|
||
</code></pre>
|
||
|
||
<h3>Serialize</h3>
|
||
|
||
<pre><code>class User < ActiveRecord::Base
|
||
serialize :preferences
|
||
end
|
||
|
||
user = User.create(:preferences => { "background" => "black", "display" => large })
|
||
</code></pre>
|
||
|
||
<p>You can also specify a class option as the second parameter that’ll raise an
|
||
exception if a serialized object is retrieved as a descendant of a class not in
|
||
the hierarchy.</p>
|
||
|
||
<pre><code>class User < ActiveRecord::Base
|
||
serialize :preferences, Hash
|
||
end
|
||
|
||
user = User.create(:preferences => %w( one two three ))
|
||
User.find(user.id).preferences # raises SerializationTypeMismatch
|
||
</code></pre>
|
||
|
||
<h2>Overriding accessors</h2>
|
||
|
||
<pre><code>class Song < ActiveRecord::Base
|
||
# Uses an integer of seconds to hold the length of the song
|
||
|
||
def length=(minutes)
|
||
write_attribute(:length, minutes.to_i * 60)
|
||
end
|
||
|
||
def length
|
||
read_attribute(:length) / 60
|
||
end
|
||
end
|
||
</code></pre>
|
||
|
||
<ul>
|
||
<li>http://api.rubyonrails.org/classes/ActiveRecord/Base.html</li>
|
||
</ul>
|
||
|
||
<h2>Callbacks</h2>
|
||
|
||
<pre><code>after_create
|
||
after_initialize
|
||
after_validation
|
||
after_save
|
||
after_commit
|
||
</code></pre>
|
||
|
||
|
||
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.4/jquery.min.js'></script>
|
||
<script src="http://cachedcommons.org/cache/prettify/1.0.0/javascripts/prettify-min.js"></script>
|
||
<script>$("pre").addClass("prettyprint");</script>
|
||
<script>prettyPrint();</script>
|
||
</body>
|
||
</html>
|