Rails has_many :through association: 通过外键检索外部属性
Rails has_many :through association: retrieve foreign attribute through foreign key
在我们的 Rails 应用程序中,有 3 个模型:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
这里是相应的迁移:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
编辑:这也是我们的UsersController
:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: [:index, :destroy]
def index
@users = User.paginate(page: params[:page], :per_page => 10)
end
def show
@user = User.find(params[:id])
@administrations = @user.administrations
@calendar = current_user.calendars.build if logged_in?
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email,
:password, :password_confirmation)
end
# Before filters
# Confirms the correct user.
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.try(:admin?)
end
end
一旦用户登录(身份验证系统已经启动并且 运行),我们希望在他的个人资料 (users#show) 上显示他创建的所有日历。
我们已经为数据库播种了以下实例:
User.create!(first_name: "Andy") # This user's id is 1.
Calendar.create!(name: "CalendarA")
Calendar.create!(name: "CalendarB")
Calendar.create!(name: "CalendarC")
Administration.create!(user_id: 1, calendar_id: 1, role: "Creator")
Administration.create!(user_id: 1, calendar_id: 2, role: "Editor")
Administration.create!(user_id: 1, calendar_id: 3, role: "Viewer")
然后,我们创建了一个 _administration.html.erb
部分:
<li id="administration-<%= administration.id %>">
<span class="name"><%= administration.calendar_id %></span>
</li>
并将其包含在我们的用户 show.html.erb
文件中:
<p><%= @user.first_name %>'s calendars</p>
<% if @user.administrations.any? %>
<%= render @administrations %>
<% end %>
这是有效的,我们得到:
Andy's calendars:
- 1
- 2
- 3
但是,我们希望为每个用户获取的不仅是他日历的 id
,还有他们的 name
,如下所示:
Andy's calendars:
- 1 CalendarA
- 2 CalendarB
- 3 CalendarC
所以我们尝试更新 _administration.html.erb
部分如下:
<li id="administration-<%= administration.id %>">
<span class="name"><%= administration.calendar_id.name %></span>
</li>
这会导致以下错误:
NoMethodError in UsersController#show
undefined method `name' for 1:Fixnum
Application Trace | Framework Trace | Full Trace
app/views/administrations/_administration.html.erb:2:in `_app_views_administrations__administration_html_erb__2225316747000531998_70329866860100'
app/views/users/show.html.erb:32:in `_app_views_users_show_html_erb___891585127045041471_70329832995240'
我们如何通过连接 administration
模型中的外键 calendar_id
从 calendar
模型访问 "foreign" 属性 name
?
如果您的关联设置正确,administration.calendar.name
应该可以工作。
或者,您可以将此方法添加到 Administration
:
def calendar_name
calendar.name
end
然后调用 administration.calendar_name
在我们的 Rails 应用程序中,有 3 个模型:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
这里是相应的迁移:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
编辑:这也是我们的UsersController
:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: [:index, :destroy]
def index
@users = User.paginate(page: params[:page], :per_page => 10)
end
def show
@user = User.find(params[:id])
@administrations = @user.administrations
@calendar = current_user.calendars.build if logged_in?
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email,
:password, :password_confirmation)
end
# Before filters
# Confirms the correct user.
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.try(:admin?)
end
end
一旦用户登录(身份验证系统已经启动并且 运行),我们希望在他的个人资料 (users#show) 上显示他创建的所有日历。
我们已经为数据库播种了以下实例:
User.create!(first_name: "Andy") # This user's id is 1.
Calendar.create!(name: "CalendarA")
Calendar.create!(name: "CalendarB")
Calendar.create!(name: "CalendarC")
Administration.create!(user_id: 1, calendar_id: 1, role: "Creator")
Administration.create!(user_id: 1, calendar_id: 2, role: "Editor")
Administration.create!(user_id: 1, calendar_id: 3, role: "Viewer")
然后,我们创建了一个 _administration.html.erb
部分:
<li id="administration-<%= administration.id %>">
<span class="name"><%= administration.calendar_id %></span>
</li>
并将其包含在我们的用户 show.html.erb
文件中:
<p><%= @user.first_name %>'s calendars</p>
<% if @user.administrations.any? %>
<%= render @administrations %>
<% end %>
这是有效的,我们得到:
Andy's calendars:
- 1
- 2
- 3
但是,我们希望为每个用户获取的不仅是他日历的 id
,还有他们的 name
,如下所示:
Andy's calendars:
- 1 CalendarA
- 2 CalendarB
- 3 CalendarC
所以我们尝试更新 _administration.html.erb
部分如下:
<li id="administration-<%= administration.id %>">
<span class="name"><%= administration.calendar_id.name %></span>
</li>
这会导致以下错误:
NoMethodError in UsersController#show
undefined method `name' for 1:Fixnum
Application Trace | Framework Trace | Full Trace
app/views/administrations/_administration.html.erb:2:in `_app_views_administrations__administration_html_erb__2225316747000531998_70329866860100'
app/views/users/show.html.erb:32:in `_app_views_users_show_html_erb___891585127045041471_70329832995240'
我们如何通过连接 administration
模型中的外键 calendar_id
从 calendar
模型访问 "foreign" 属性 name
?
administration.calendar.name
应该可以工作。
或者,您可以将此方法添加到 Administration
:
def calendar_name
calendar.name
end
然后调用 administration.calendar_name