背景:
- 最近比较闲,想学习ruby on rails
- 于是找到了https://www.railstutorial.org 上的首推教程《Ruby on Rails Tutorial》
这本书第一章和第二章讲了2个基本demo,实在没啥意思,姑且略过. 从第三章开始到第十二章是从0到1实现了一个类似Twitter的简单社交网站(首页,登录注册,发布推文,关注等功能). 怎么样是不是很棒?
但是这个本书实在讲得过于详细,对于我这种本身没有那么多时间(也没那么多耐心)去一点一点看下来的童鞋,看着实在太着急了,于是准备快速整理下(把里面的干货和代码提取出来),方便大家可以分分钟coding出这个demo出来.
当然真正学习还是要看原教程,我这个只是"扒皮版本".
原文链接
RUBY ON RAILS TUTORIAL
https://www.railstutorial.org/book/static_pages
他们的github:
railstutorial/sample_app_rails_4
https://github.com/railstutorial/sample_app_rails_4
ruby学习框架图
第3-7章节见:
[Ruby]RUBY ON RAILS TUTORIAL 的搬运工之一
第8-10章节见:
[Ruby]《Ruby on Rails Tutorial》的搬运工之二
下面是第11章开始
11. User microposts
用户的推文功能实现:
11.1 A Micropost model
a). 首先我们需要新建一个基本model
rails generate model Micropost content:text user:references
b). 数据增加index
//db/migrate/[timestamp]_create_microposts.rb
class CreateMicroposts < ActiveRecord::Migration
def change
create_table :microposts do |t|
t.text :content
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :microposts, [:user_id, :created_at]
end
end
11.1.3 User/Micropost associations
做数据关联:
polen:
这个是ruby的一个特性.ruby希望我们的数据库原则上不要做强关联(当然你非要这么干也没人拦着你),但是它提供了一种模型关联的方法,这种关联方法有什么用? 就是数据之间的操作可以更简单也更直观一些:
举例:我们有2个model,customer和order.
如果不做关联,新建个订单需要:
@order = Order.create(order_date: Time.now, customer_id: @customer.id)
但二者如果做了belongs_to和has_many关联,那么就可以写成:
@order = @customer.orders.create(order_date: Time.now)
比较常见的几种关联关系有:
belongs_to # 一对多,与 has_many,has_one 套用
has_one # 一对一
has_many # 一对多的另外一方
has_and_belongs_to_many # 多对多
参照:Rubyonrails.org:Active Record 关联
11.1.4 Micropost refinements
这里是对11.1.3的完善
a). 用户的推文信息很多的时候,我们希望默认是按照"最近发布的排在最前面"的原则(即:时间倒序)
怎么实现呢? 很简单,设置个default_scope
.
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end
b). 用户和自己的推文是绑定的,如果用户被删除了,那么他对应的推文也需要全部删除,这个总不能来个for循环删除吧,那怎么办呢?
刚在不是做了关联么,加一句话即可dependent: :destroy
:
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
attr_accessor :remember_token, :activation_token, :reset_token
...
polen:
dependent用于:设置销毁拥有者时要怎么处理关联对象
包含以下几种参数:
- destroy:也销毁关联对象;
- delete:直接把关联对象对数据库中删除,因此不会执行回调;
- nullify:把外键设为 NULL,不会执行回调;
- restrict_with_exception:有关联的对象时抛出异常;
- restrict_with_error:有关联的对象时,向拥有者添加一个错误;
如果在数据库层设置了 NOT NULL约束,就不能使用 :nullify
选项。如果 :dependent选项没有销毁关联,就无法修改关联对象,因为关联对象的外键设置为不接受 NULL.
11.2 Showing microposts
数据层做好了,开始做UI层展示了:
11.2.1 Rendering micro posts
a). 新建controller,新建html
//新建一个controller
rails generate controller Microposts
//新建_micropost.html.erb,参照之前的_user.html.erb代码模式
touch app/views/microposts/_micropost.html.erb
b). 画基础UI (相当于iOS的tableviewCell )
//app/views/microposts/_micropost.html.erb
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<%= link_to micropost.user.name, micropost.user %>
<%= micropost.content %>
c). 界面展示controller和show.html.erb:
//app/controllers/users_controller.rb
...
def show
@user = User.find(params[:id])
@micropost = @user.miscropost.paginate(page: params[:page])
end
...
//app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<% if @user.micropost.any? %>
Miscroposts (<%= @user.micropost.count %>)
<%= render @microposts %>
<%= will_paginate @microposts %>
<% end %>
d). 布局调整一下,不然目前是这样的
//app/assets/stylesheets/custom.css.scss
...
/* microposts */
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
}
.user {
margin-top: 5em;
padding-top: 0;
}
.content {
display: block;
margin-left: 60px;
img {
display: block;
padding: 5px 0;
}
}
.timestamp {
color: $gray-light;
display: block;
margin-left: 60px;
}
.gravatar {
float: left;
margin-right: 10px;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.picture {
margin-top: 10px;
input {
border: 0;
}
}
调整之后是这样子:
11.3 Manipulating microposts
接下来就是对推文的增删操作了.
做之前先增加路由:
//config/routes.rb
...
resources :microposts, only: [:create, :destroy]
11.3.1 Micropost access control
a). 首先要查看推文的话,需要检查是否登录,之前是因为只是用户信息界面需要,所以我们的logged_in_user
方法只写在了users_controller
里面,现在作为公用,我们需要移到application_controller
里面
//app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
private
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
b). 作为公用之后,我们就可以用起来了:
MicropostsController增加action,以及对应的登录状态检查
//app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
end
def destroy
end
end
11.3.2 Creating microposts
a). controller 中添加create action
//app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end
b). home 页添加发布消息的UI
//app/views/static_pages/home.html.erb
<% if logged_in? %>
<% else %>
Welcome to the Sample App
This is the home page for the
Ruby on Rails Tutorial
sample application.
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
<%= link_to image_tag("rails.png", alt: "Rails logo"),
'http://rubyonrails.org/' %>
<% end %>
c). 新增_user_info.html.erb和_micropost_form.html.erb
//app/views/shared/_user_info.html.erb
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<%= current_user.name %>
<%= link_to "view my profile", current_user %>
<%= pluralize(current_user.microposts.count, "micropost") %>
//app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
d). StaticPagesController添加一个变量micropost
//app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
@micropost = current_user.microposts.build if logged_in?
end
e) .错误提示之前只是用于user,现在需要修改为通用型,所以将@user
修改为object
:
//app/views/shared/_error_messages.html.erb
<% if object.errors.any? %>
The form contains <%= pluralize(object.errors.count, "error") %>.
<% object.errors.full_messages.each do |msg| %>
- <%= msg %>
<% end %>
<% end %>
然后将
- users/new.html.erb,
- users/edit.html.erb,
- password_resets/edit.html.erb
中的错误处理:
<%= render 'shared/error_messages' %>
修改为:
<%= render 'shared/error_messages', object: f.object %>
11.3.3 A porto-feed
polen:
feed这个东西是什么鬼?也想不出具体的定义,其实就是个list .想深入了解的童鞋可以参考:
知乎:Feed 除了 timeline 形式,还有没有更好的内容展示方式...
a). user model 增加def feed
//app/models/user.rb
class User < ActiveRecord::Base
...
# Defines a proto-feed.
# See "Following users" for the full implementation.
def feed
Micropost.where("user_id = ?", id)
end
private
...
b). static_pages_controller 增加@feed_items
//app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
...
c). 写个可复用的_feed.html.erb
//app/views/shared/_feed.html.erb
<% if @feed_items.any? %>
<%= render @feed_items %>
<%= will_paginate @feed_items %>
<% end %>
d). home页把feed加进去
//app/views/static_pages/home.html.erb
<% if logged_in? %>
Micropost Feed
<%= render 'shared/feed' %>
<% else %>
...
e). 针对提交失败,@feed_items
找不到,所以要打个预防针:
//app/controllers/microposts_controller.rb
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
11.3.4 Destroying microposts
这块开始搞删除啦...
a). 增加个删除的按钮
//app/views/microposts/_micropost.html.erb
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<%= link_to micropost.user.name, micropost.user %>
<%= micropost.content %>
b). controller 中增加def destroy
和def correct_user
//app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
def destroy
@micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || root_url
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
def correct_user
@micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if @micropost.nil?
end
end
11.4 Micropost images
没有图像,用户还是怎么"装高冷"?...
11.4.1 Basic image upload
a). 首先引入几个库
gem 'carrierwave', '0.10.0'
gem 'mini_magick', '3.8.0'
gem 'fog', '1.36.0'
b). 建一个uploader以及数据库加字段picture:string
rails generate uploader Picture
rails generate migration add_picture_to_microposts picture:string
c). model 中加image
//app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
end
polen:
这里用到了mount_uploader
这个方法,这个方法用于将model的属性和上传者绑定.(其实就是绑定数据库的某一列)
官方解释看这里:/CarrierWave/Mount
d). UI可以改起来啦,在"写新推文"的界面,添加上传照片的button
//app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
<%= f.submit "Post", class: "btn btn-primary" %>
<%= f.file_field :picture %>
<% end %>
e). MicropostsController中参数也要把picture带进去
//app/controllers/microposts_controller.rb
def micropost_params
params.require(:micropost).permit(:content, :picture)
end
f). 继续改UI,找个展示图片的地方
//app/views/microposts/_micropost.html.erb
...
<%= micropost.content %>
<%= image_tag micropost.picture.url if micropost.picture? %>
...
11.4.2 Image validation
以防用户乱传各种苍老师的照片,我们需要加一些限制和校验...
a). 格式限制:picture_uploader.rb中关于格式限制的代码解注
//app/uploaders/picture_uploader.rb
def extension_white_list
%w(jpg jpeg gif png)
end
b). 大小限制
//app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
validate :picture_size
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB")
end
end
end
如果超过大小限制,我们需要来个alert提示.
//app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost, html: { multipart: true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
<%= f.submit "Post", class: "btn btn-primary" %>
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
<% end %>
11.4.3 Image resizing
调整大小,不然丑的不得了...(图片默认都是width=1240)
PictureUploader中加一下限制即可:
首先要安装一下imagemagick:
brew install imagemagick
然后resize_to_limit进行限制:
//app/uploaders/picture_uploader.rb
class PictureUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
process resize_to_limit: [400, 400]
...
可以看一下,限制前后的对比:
12. Following users
真正的社交属性来了啊
12.1 The Relationship model
关注(或者说互粉)这种功能,按照之前的逻辑是建个表,userA,has_many
(model的关联属性) user.following表。但是这种的缺点是二者的耦合性太强,一旦用户改个名字或者什么,那对应的表都要修改,这种影响的范围太大. 于是考虑到,中和下,建立个关联表,作为中间过渡,这样可以确保,用户之间的关联只通过这个relationship表来查找,而不影响user.following表. 多说无意,一图解真相:
具体做起来:
a). 生成一个Relationship的model:
rails generate model Relationship follower_id:integer followed_id:integer
b). 修改表结构,增加几个字段:
//db/migrate/[timestamp]_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps null: false
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
c). 数据库迁移
rake db:migrate
12.1.2 User/relationship associations
12.1.4 Followed users && 12.1.4 Followers
followeds 看起来太丑,于是用following:
followers:自己的粉丝
a). 设置关联属性
//app/models/user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
...
b). 然后我们需要在user model中添加关注方法,就是,真正实现关联的action
//app/models/user.rb
class User < ActiveRecord::Base
...
def feed
Micropost.where("user_id = ?", id)
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
private
...
polen:
如果大家平时测试中出现类似undefined method "xxx"
这种错误,常见的是2个原因:
- 对应的model中没有设置关联属性,像如下:
has_many :following, through: :active_relationships, source: :followed
- 对应的model中没有定义相关方法,像如下:
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
12.2 A web interface for following users
12.2.2 Stats and a follow form
a). 路由加一下:
//config/routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
...
b). 写基础UI
//app/views/shared/_stats.html.erb
<% @user ||= current_user %>
c). home页增加follower相关
//app/views/static_pages/home.html.erb
<% if logged_in? %>
Micropost Feed
<%= render 'shared/feed' %>
<% else %>
...
d) .改布局
...
//app/assets/stylesheets/custom.css.scss
.gravatar {
float: left;
margin-right: 10px;
}
.gravatar_edit {
margin-top: 15px;
}
.stats {
overflow: auto;
margin-top: 0;
padding: 0;
a {
float: left;
padding: 0 10px;
border-left: 1px solid $gray-lighter;
color: gray;
&:first-child {
padding-left: 0;
border: 0;
}
&:hover {
text-decoration: none;
color: blue;
}
}
strong {
display: block;
}
}
.user_avatars {
overflow: auto;
margin-top: 10px;
.gravatar {
margin: 1px 1px;
}
a {
padding: 0;
}
}
.users.follow {
padding: 0;
}
/* forms */
...
e). 继续写几个基础空间:
//app/views/users/_follow_form.html.erb
<% unless current_user?(@user) %>
<% if current_user.following?(@user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
<% end %>
//app/views/users/_follow.html.erb
<%= form_for(current_user.active_relationships.build) do |f| %>
<%= hidden_field_tag :followed_id, @user.id %>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
//app/views/users/_unfollow.html.erb
<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class: "btn" %>
<% end %>
f). 最后show里面完善下:
//app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<% if @user.microposts.any? %>
Microposts (<%= @user.microposts.count %>)
<%= render @microposts %>
<%= will_paginate @microposts %>
<% end %>
12.2.3 Following and followers pages
a). controller 添加following和followers方法
//app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
...
def following
@title = "Following"
@user = User.find(params[:id])
@users = @user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
@title = "Followers"
@user = User.find(params[:id])
@users = @user.followers.paginate(page: params[:page])
render 'show_follow'
end
...
b). show的UI可以做起来了
//app/views/users/show_follow.html.erb
<% provide(:title, @title) %>
<%= @title %>
<% if @users.any? %>
<%= render @users %>
<%= will_paginate %>
<% end %>
12.2.4 A working follow button the standard way
a). Relationships
rails generate controller Relationships
b). 写create方法和destroy方法
//app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
user = User.find(params[:followed_id])
current_user.follow(user)
redirect_to user
end
def destroy
user = Relationship.find(params[:id]).followed
current_user.unfollow(user)
redirect_to user
end
end
12.2.5 A working follow button with Ajax
ruby on rails 怎么玩Ajax呢:
a). 引入Ajax
ruby中使用form_for
后面加个remote: true
,就会自动引入Ajax
polen:
深入学习看这里:Ruby on Rails 實戰聖經:Ajax 應用程式
所以我们的代码是这样的:
b). RelationshipsController中加入相应js的代码
首先看这样一段代码:
respond_to do |format|
format.html { redirect_to user }
format.js
end
polen:
- 这里的
respond_to
意思是对不同的请求进行不同的处理(目的是对不同的浏览器做兼容---比如有些浏览器禁用了JavaScript).
意思是如果浏览器请求的是html,那么我们就xxxx处理;如果请求的是js,就xxxx处理,当然也可以加xml等等...
可以参考:ActionController::MimeResponds - ruby还有个
respond_to?
, 其实就多加了个问号,但是目的就完全不一样了,这个是看看对象是否有对应的方法函数.
可以参考: Confused about 'respond_to' vs 'respond_to?'
这个和iOS里的respondsToSelector是一样的:
- (BOOL)respondsToSelector:(SEL)aSelector;
* `do |format| `这种格式,在ruby里,"| ... |"里的内容表示参数
* 说到do ,额外插一句ruby的for循环,一直忘记说了.
其他大部分语言喜习惯用`for in xxx`的模式,这个在ruby也可以用,但是推荐用` xxx.each do |item|`,这样更"ruby"一些.
另外,二者的区别是for循环之后,item的值还是在的;但each循环之后,item值当场就被释放了.
可以参考:[“for” vs “each” in Ruby](http://stackoverflow.com/questions/3294509/for-vs-each-in-ruby)
>```
# way 1
@collection.each do |item|
# do whatever
end
># way 2
for item in @collection
# do whatever
end
c). 添加代码:
# app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
# @user = User.find(params[:followed_id])
@user = User.find(params[:relationship][:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end
polen:
纠错:
关于获取当前这个@user
这里原文章是@user = User.find(params[:followed_id]
,但这实际是有问题的.如果直接看服务端log,能看到我们的请求是:
Parameters: {
"utf8"=>"✓",
"relationship"=>{
"followed_id"=>"4"
},
"commit"=>"Follow"}
可以看到followed_id
是在relationship
下面的(相当于2个dictionary 包裹起来的),所以我们的解析应该是params[:relationship][:followed_id]
所以代码应该是:
@user = User.find(params[:relationship][:followed_id])
服务端log如下:
同时看他们github的代码这一行也是如此:
railstutorial/ sample_app_rails_4
d). 写最终的js文件(ajax的本质是最终能调用js文件实现局部刷新):
polen:
这里还是要做个纠正:
文章里js文件结尾是带分号的";"
的,但是实际跑起来还是会报错的:
ActionView::Template::Error
(Missing partial user/_unfollow with
{:locale=>[:en], :formats=>[:js, :html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}.
Searched in:
...
就是找不到_unfollow.html.erb
这里的解决方案是 去掉分号,查看了他们的源码也是如此:
至于为什么?anyone knows?
e). debug的tips:
polen:
这里说个debug的小tip:
因为之前测试的时候,是不是会报错,出现udefined method等错误,所以有时候需要检查下当前的user或者current_user
这个如果只是调试的话,代码里加一行<%= debug current_user%>
即可,像这样:
看到的结果是这样子( 可以详细的看到user的debug信息):
12.3 The status feed
让我们用户的feed,不只是自己的推文,还有其他用户的(不就是个"朋友圈"么).
产品经理说要做成这样子:
12.3.2 A first feed implementation
第一步,所有following_ids里的我们都添加进来
//app/models/user.rb
...
def feed
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
end
...
12.3.3 Subselects
做个小小的优化,提升sql效率:
//app/models/user.rb
...
def feed
following_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
Micropost.where("user_id IN (#{following_ids})
OR user_id = :user_id", user_id: id)
end
...
Github:
本文所有的代码已上传github:
polegithub/rails_sample_app_polen
相关:
[Ruby]《Ruby on Rails Tutorial》的搬运工之一
[Ruby]《Ruby on Rails Tutorial》的搬运工之二
插一曲:
万万没想到看完这本书的时候,才发现有中文版本,好吧
默默的写在这里了(感谢安道童鞋的翻译):
Ruby on Rails 教程: 通过 Rails 学习 Web 开发
by poles