目录部分
The Rails Command Line
model
Active Record Basics
Active Record Migrations
Active Record Validations
Active Record Callbacks
Active Record Associations
Active Record Query Interface
Active Model Basics
controllers
Action Controller Overview
Rails Routing from the Outside In
digging deeper
Active Job Basics
The Asset Pipeline
正文部分
The Rails Command Line
rails server -e production -p 4000 #指定环境和端口
rails new demo --database=mysql #指定数据库
rails g scaffold Article name:string #使用scaffold,资源为单数形式
rails g controller Articles index #使用controller为复数形式
rails g model Article name #使用model为单数形式
#和其他表格进行关联,tag has_many artilces
rails g modle Artilce name:string tag:references
#升级到rails5.0之后,之前的rake命令变为rails命令(成为默认的执行命令)
rake routes => rails routes
rake demo.rake =>rails demo.rake #执行rake任务,在lib/tasks中
Active Record Basics
model和数据库名称要相互对应
#普通单数形式
model:Article #首字母大写
database:articles
#非普通单数形式
model:Person
database:people
#多字段形式
model:LineItem #camelcase形式
database:line_items
主键和外键
主键:id,自动生成
外键:singularized_table_name_id,比如article_id
#在命名终端生成主键和外键
rails g model Article name:string #自动生成主键id
rails g model Comment name:string article:references #生成外键article_id(注意格式)
Active Record Migrations
创建表单和字段,删除表单
#rails g model Product name:string
#生成表单和字段
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.string :name
t.timestamps
end
end
end
#rails g migration remote_product
class RemoteProcut < ActiveRecord::Migration[5.0]
def change
drop_table :prefaces
end
end
生成字段、删除字段
#默认已经有articles这个表单,生成name字段
#rails g migration addnametoarticle
class Addnametoarticle < ActiveRecord::Migration[5.0]
def change
add_column :articles, :name, :string
end
end
#删除字段
#rails g migration removenameformarticle
class Addnametoarticle < ActiveRecord::Migration[5.0]
def change
remove_column :articles, :name, :string
end
end
增加索引,删除索引
#默认已经有articles的name字段
#rails g migration addindextoname
class Addindextoname < ActiveRecord::Migration[5.0]
def change
add_index :articles, :name
end
end
#查看数据中那些字段存在索引
show index from articles
#查看指定字段是否存在索引
show index from articles where column_name like 'name'
#删除索引
class Removeindex < ActiveRecord::Migration[5.0]
def change
remove_index :articles, :name
end
end
建立关联和删除关联
#比如已经有artilces,需要实现article has many tags,因此建立tag model 最简单的关联:
rails g model Tag name:string article:references
#上面在tags表中会出现article_id这个字段
#使用migration进行references
rails generate migration AddUserRefToProducts user:references
class AddUserRefToProducts < ActiveRecord::Migration[5.0]
def change
add_reference :products, :user, index: true, foreign_key: true
end
end
#删除关联
自己没有实现
Active Record Callbacks
callback是指回调的意思,当对象状态改变的时候,这些回调方法会被触发,对象的状态有三种,create,update,destroy,回调的方法调用形式如下:
#第一种,使用预定义的回调函数,里面定义块的内容
class User < ApplicationRecord
before_validation do |user|
puts "this is the test"
end
end
#第二种,使用预定义的回调函数注册回调方法
class User < ApplicationRecord
before_validation :test
#可以指定某个action上使用验证
before_validation :test, on: :create
#可以指定多个action上使用验证
before_validation: test, on: [:create, :update]
private
def test
puts "this is the test"
end
end
对象改变触发的回调函数如下:
#当create时
before_validation
after_validation
before_save
around_save
after_save
before_create
around_create
after_create
after_commit/after_rollback
#当update时
before_validation
after_validation
before_save
around_save
after_save
before_update
around_update
after_update
after_commit/after_rollback
#当destroy时
before_destroy
around_destroy
after_destroy
after_commit/after_rollback
#initialize
after_initialize #对象初始化和从数据库中load对象时触发
#find
after_find #从数据库中load对象时触发
#touch
after_touch #对象进行touch时触发
具体的触发和不触发方法的动作
#create, update, destroy
create
create!
decrement!
destroy
destroy!
destroy_all
increment!
save
save!
save(validate: false)
toggle!
update_attribute
update
update!
valid?
#find
all
first
find
find_by
find_by_*
find_by_*!
find_by_sql
last
#跳过触发动作
decrement
decrement_counter
delete
delete_all
increment
increment_counter
toggle
touch
update_column
update_columns
update_all
update_counters
Active Record Query Interface
获得单个值
find #通过id获得值,也通过通过id数组获得值
Article.find(1)
Article.find([1,2]) #Article.find(1,2)也是可以的
take #没有知名排序取值
Article.take #默认取1个值
Article.take(2) #取两个值
first #默认按照id的升序取第一个值
Article.first #如上
Article.first(3) #取前三个值
Article.order(:name).first #改变默认排序,取第一个值
last #默认按照id的降序取第一个值
Artilce.last
Artilce.last(3)
Article.order(:name).last
find_by #通过相应的字段属性取值
Article.find_by(name: "jayzen")
处理大数据量的情况,目的是避免超出内存容量
#方法依次是
find_each
find_in_batch
#可选的参数包括
batch_size
start
finish
#两者的区别
find_each #每次数据传递一部分
find_in_batch #数据一次性传递,但是按照数组块的形式
使用where进行条件查询
#纯字符查询会出现SQL注入问题,不采用
Client.where("orders_count = '2'")
#数组查询
#单个数组查询
Client.where("orders_count = ?", params[:orders])
#多个数组查询
Client.where("orders_count = ? AND locked = ?", params[:orders], false)
#使用占位符进行数组查询
Client.where("created_at >= :start_date AND created_at <= :end_date",
{start_date: params[:start_date], end_date: params[:end_date]})
#使用hash条件查询
#单个hash值
Client.where(locked: true)
#范围hash值
Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
#数组hash值
Client.where(orders_count: [1,3,5])
#使用not条件查询
Client.where.not(locked: true)
使用order进行排序
#单个字段使用order进行排序,并且使用desc或者asc指定排序方式
Article.order(name: :desc)
#多个字段使用order进行排序
Article.order(name: :desc, content: :asc)
#使用链式排序
Article.order(name: :desc).order(content: :asc)
使用select选择特定的字段
#只选择两个字段内容
Article.select("name, content")
#选择字段的属性内容,不重复出现
Article.select("name").distinct
使用limit和offset指定返回的个数和开始的位置
Article.limit(5) #最多返回5条记录,默认返回最初的5条
Article.limit(5).offset(30) #从第31条开始返回记录,最多返回5条记录
使用group进行分组
#获得某一个时间点的订单数量
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")
#统计不同状态值的累计量
Order.group(:status).count
使用having进行条件判断
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)").having("sum(price) > ?", 100)
对条件进行改变
#unscope取消条件
Article.where('id > 10').limit(20).order('id
asc').unscope(:order) #取消order条件
#使用only重新制定选择条件
Article.where('id > 10').limit(20).order('id desc').only(:order, :where)
#使用reorder进行条件重新制定
class Article < ApplicationRecord
has_many :comments, -> { order('posted_at DESC') }
end
Article.find(10).comments.reorder('name')
#使用reverse_order改变排列的方式
Client.where("orders_count > 10").order(:name).reverse_order
#使用rewhere改变where的排列方式
Article.where(trashed: true).rewhere(trashed: false)
返回空值
Article.none #返回空值
设置对象可读,在写入的时候会报错
client = Client.readonly.first
client.visits += 1
client.save #会报错ActiveRecord::ReadOnlyRecord exception
数据库中实现join(inner join)和left join的区别
方式和rails中的joins和left_outer_joins一致
#join(inner join)
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders
ON Persons.Id_P = Orders.Id_P
ORDER BY Persons.LastName
#结果:返回的完全匹配的值,即是必须满足Persons.Id_P = Orders.Id_P
#left join(left outer join)
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
LEFT JOIN Orders
ON Persons.Id_P=Orders.Id_P
ORDER BY Persons.LastName
#首先返回Persons.Id_P = Orders.Id_P的值,如果Orders.Id_P不存在,则只返回左表中的值即可。
悲观锁(pessimistic lock)和乐观锁(optimistic lock)
optimistic lock:认为冲突比较少,用户在检查的更新的检查lock_version的值时候变动,变动即返回错误。应用在实际冲突比较少的场景。举例:rails中model表单存在可选的lock_version字段。
pessimistic lock:认为每一次更新都存在冲突,都有一个锁定和开锁的过程,这样子程序开销比较大,引用在冲突比较多的场景。
Rails Routing from the Outside In
作用:链接url到code;生成url
1、resource routing
resources :photos(复数资源)
GET /photos photos#index photos_path
GET /photos/new photos#new new_photo_path
POST /photos photos#create
GET /photos/:id photos#show
GET /photos/:id/edit photos#edit edit_photo_path(:id)
PATCH/PUT /photos/:id photos#update
DELETE /photos/:id photos#destroy photo_path(:id)
上面提到的都是复述资源,下面讲述单数资源
resource :geocoder
GET /geocoder/new geocoders#new new_geocoder_path
POST /geocoder geocoders#create
GET /geocoder geocoders#show geocoder_path
GET /geocoder/edit geocoders#edit edit_geocoder_path
PATCH/PUT /geocoder geocoders#update
DELETE /geocoder geocoders#destroy
对于单例资源生成表格,需要制定url
form_for @geocoder, url: geocoder_path do |f|
...
对于namespace路由、嵌套路由、浅嵌套(shallow nesting)在文档中有叙述,但是自己用到的不多,所以遇到的时候在参考guides即可。Routing concerns是将常用到的路由打包,以备后用。
增加的三个restful形式的路由
#member
resources :photos do
member do
get 'preview'
end
end
=>/photos/1/preview
preview_photo_url
params[:id]
#collection
resources :photos do
collection do
get 'search'
end
end
=>/photos/search
search_photos_url
#new
resources :comments do
get 'preview', on: :new
end
#/comments/new/preview
preview_new_comment_path
2、non resourceful routes
Active Job Basics
应用场景:在用邮箱注册的过程中,给邮箱发送邮件这个动作应该放在后台进行。
active job可以使用的adapter有很多,比如delayed job,resque和sidekiq,后两者需要安装redis,其中sidekiq是在resque基础上的改进,但是性能方面要优于resque,但是sidekiq的过程中要注意线程安全问题。
active job的其中一个adapter是sidekiq,而这个软件需要安装redis。
其中redis的安装和启动过程如下:
#目前redis的版本是3.2
brew install redis
#启动redis
redis-server /usr/local/etc/redis.conf
其中sidekiq的安装和启动的过程如下:
#安装就是添加gem
gem 'sidekiq'
#启动就是终端输入如下代码
sidekiq
项目启动之前要同时启动redis和sidekiq,下面执行代码中内容:
#生成scaffold
rails g scaffold Article name:string
#生成job文件
rails g job add_article
#修改jobs/add_artilce_job.rb文件
class AddArticleJob < ApplicationJob
queue_as :default
def perform(*args)
sleep 10
10.times do |index|
article = Article.new
artilce.name = "name#{index}"
article.save
end
end
#修改app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def index
AddArticleJob.perform_later
@articles = Article.all
end
end
在没有使用sidekiq作为adapter的情况下,会使用Active Job Inline作为默认的adapter,但是这adapter不是异步的,按照代码执行顺序进行延后,但是如果采用sidekiq作为adapter的情况,可以异步,只需要进行如下的配置:
module JobDemo
class Application < Rails::Application
config.active_job.queue_adapter = :sidekiq
end
end
可以通过log的代码关注执行情况,
The Asset Pipeline
首先需要关注的是css文件和javascript文件如何进行组织的问题。
assets/stylesheets/application.css文件内容
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
assets/stylesheets/application.js文件
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
上文分别给出了application.js和application.css的说明,其中
//= require jquery
//= require jquery_ujs
//= require turbolinks
会从三个地方寻找对应的文件,分别是app/assets,lib/assets和vender/assets三个文件夹中,require_tree .。表示在app/assets文件中js和css文件会被自动合并到application.css或者application.js文件中,在lib/assets和vender/assets中的文件除非在application.js或者application.css文件中按照如上的方式被引入,不然无效。