Rails - 如何显示父对象的属性

Rails - How to show attribute of parent object

我正在用 Rails 4.

开发一个应用

我正在尝试了解如何使用关联以及如何显示关联模型的属性。我正在努力掌握这些概念。

我有一个项目模型和一个project_invitations模型。

协会是:

Projects has many project invitations
Project invitations belong to project

在我的 project_invitation 节目中,我试图显示项目属性以及项目创建者属性(存储在用户配置文件 table 中)。

我卡住了。

在project_invitation节目中,我有:

You're invited to participate in <%= project_invitation.project.title %>

我也试过:

You're invited to participate in <%= @project_invitation.project.title %>

当我尝试 ^^ 我得到这个错误:

undefined method `title' for nil:NilClass

在我的项目 table 中,我有一个名为 :title 的属性。

我希望项目邀请显示显示被邀请者的项目名称。

我尝试这样做的方式有什么问题?

我的 project_invitations 控制器具有以下内容(由脚手架生成):

class ProjectInvitationsController < ApplicationController
  before_action :set_project_invitation, only: [:show, :edit, :update, :destroy]

  # GET /project_invitations
  # GET /project_invitations.json
  def index
    @project_invitations = ProjectInvitation.all
  end

  # GET /project_invitations/1
  # GET /project_invitations/1.json
  def show
  end

  # GET /project_invitations/new
  def new
    @project_invitation = ProjectInvitation.new
  end

  # GET /project_invitations/1/edit
  def edit
  end

  # POST /project_invitations
  # POST /project_invitations.json
  def create
    @project_invitation = ProjectInvitation.new(project_invitation_params)

    respond_to do |format|
      if @project_invitation.save
        format.html { redirect_to @project_invitation, notice: 'Project invitation was successfully created.' }
        format.json { render action: 'show', status: :created, location: @project_invitation }
      else
        format.html { render action: 'new' }
        format.json { render json: @project_invitation.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /project_invitations/1
  # PATCH/PUT /project_invitations/1.json
  def update
    respond_to do |format|
      if @project_invitation.update(project_invitation_params)
        format.html { redirect_to @project_invitation, notice: 'Project invitation was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @project_invitation.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /project_invitations/1
  # DELETE /project_invitations/1.json
  def destroy
    @project_invitation.destroy
    respond_to do |format|
      format.html { redirect_to project_invitations_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_project_invitation
      @project_invitation = ProjectInvitation.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def project_invitation_params
      if can? :create, Project,
        params[:project_invitation].permit(:comment, :expiry_date, :draft, :course_credit)
      elsif can? :read, ProjectInvitation
        params[:project_invitation].permit(:student_accepted, :sponsor_accepted)
      end
    end
end

project_invitation 型号有:

class ProjectInvitation < ActiveRecord::Base

      belongs_to :project
      belongs_to :user
end

项目模型有:

class Project < ActiveRecord::Base

  include RoleModel

  #belongs_to :students, through: :courses, counter_cache: true

  has_many :project_questions, dependent: :destroy#, through: :projects
  accepts_nested_attributes_for :project_questions

  has_one :sweep
  accepts_nested_attributes_for :sweep

  has_one :educator_project_comment
  has_many :project_student_eois
  belongs_to :educator
  has_many :project_invitations
  has_one :video
  accepts_nested_attributes_for :video
  has_one :finalise, through: :sweep
  has_many :observations

  has_one :approval_request
  belongs_to :industry
  belongs_to :course

  has_and_belongs_to_many :users
  belongs_to :creator, class_name: 'User'
  has_many :profiles, through: :industry


  mount_uploader :hero_image, AvatarUploader
  mount_uploader :link_to_video_proposal, VideoUploader
end

在我的 project_invitiatons 展示页面中,我有:

<div class="containerfluid">
<%= render 'static/deviselinks'%>
  <div class="row">
    <div class="col-md-10 col-md-offset-1">
          <h1 class="header-project" style="margin-bottom:10%">You're invited to participate in <%= @project_invitation.project.try(:title) %></h1>
    </div>
   </div>

    <div class="row">
        <div class="col-md-6 col-md-offset-1">
            <div class="row">
                <div class="col-md-12" style="padding:5%">

                    <div class="invitebody">
                        <%= @project_invitation.comment %>
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="col-md-8 col-md-offset-2" style="padding:5%">
                    <div class="invitebody">
                        If you'd like to participate in this project, reply by <%= @project_invitation.expiry_date.try(:strftime, '%d %B %Y') %>
                        <br>
                        <%= render 'rsvp' %>
                    </div>
                </div>
            </div>



        </div>



        <div class="col-md-3 col-md-offset-1" style="padding: 10%">
            <div class="row">
                <div class="col-md-12">
                    <% if @creator_profile.image.present? %>
                        <%= image_tag (@creator_profile.image.profile) %>
                    <% else %>
                        <div class="generaltext">Image TBC</div>
                    <% end %>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <%= "#{@creator_profile.title} #{@creator.first_name} #{@creator.last_name}" %>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                <%= image_tag (@project.hero_image.thumb) %>
                </div>
            </div>
            <div class="row">
                <div class="col-md-12">
                    <%= @project.title %>
                </div>
            </div>
        </div>
    </div>



    <div class="formminor">
      <%= link_to 'Back', project_invitations_path %>
    </div>
</div>

我将 'try' 放在 'title' 之前,因为我在保存新邀请时遇到其他问题。我想看看这是不是我唯一的问题。我期待下一个错误是同类错误并用 'comment' 标记问题。它跳过了那个并引发了一个错误:

undefined method `image' for nil:NilClass

这对我来说很奇怪,因为我只是问是否有一件礼物。此外,该代码出现在有关评论的问题之后,那里似乎没有问题。

这有点令人困惑。

您的 .erb 文件中的代码是正确的。该错误表明没有与@project_invitation 关联的项目。我认为问题可能出在您的控制器代码中。您应该在创建操作中将特定项目分配给新 project_invitation。

试试这个代码:

  def create
    @project = Project.first  #Here I use the first project in your database, you should change it to the project you want to associate with.
    #For example, if you are creating project invitations in projects#show view, you can use the following instead
    #@project = Project.find(params[:id])
    @project_invitation = @project.project_invitations.build(project_invitation_params)

    respond_to do |format|
      if @project_invitation.save
        format.html { redirect_to @project_invitation, notice: 'Project invitation was successfully created.' }
        format.json { render action: 'show', status: :created, location: @project_invitation }
      else
        format.html { render action: 'new' }
        format.json { render json: @project_invitation.errors, status: :unprocessable_entity }
      end
    end
  end

undefined method `title' for nil:NilClass

这意味着您尝试访问的变量不存在。

Ruby 将 nil 对象视为 NilClass,因此当您对此类对象调用 方法 时,Ruby 认为您正在尝试调用不存在的方法。问题不在于方法,而在于缺少变量。


协会

这是我希望看到的代码:

#app/models/project.rb
class Project < ActiveRecord::Base
    has_many :invitations
end

#app/models/invitation.rb
class Invitation < ActiveRecord::Base
   belongs_to :user
   belongs_to :project
   delegate :title, to: :project, prefix: true #-> prevents "law of demeter"
end

您可以阅读得墨忒耳定律 here

我知道你的模型是 ProjectInvitations;我有一个规则,就是让您的模型名称尽可能简洁 - 它有助于整个应用程序的清晰度。

这将允许您调用以下内容:

#app/controllers/invitations_controller.rb
class InvitationsController < ApplicationController
   def index
      @invitations = Invitation.all
   end

   def new
      @invitation = Invitation.new
   end

   def create
      @invitation = Invitation.new invitation_params
      @invitation.save
   end

   def show
      @invitation = Invitation.find params[:id]
   end

   private

   def invitation_params
       params.require(:invitation).permit(:user,:project)
   end
end

观看次数(修复)

因为你没有解释你在调用哪个视图里面的错误变量,我不得不推测:

#app/views/invitations/index.html.erb
<% @invitations.each do |invitation| %>
   <%= invitation.project_title %>
<% end %>

#app/views/invitations/show.html.erb
<%= @invitation.project_title %>

关键是要知道您正在调用哪个视图,从而知道您可以使用哪个 @variable。我的猜测是您试图在未定义的视图中调用 @project_invitations 变量。


型号

顺便说一句,你真的不应该在一个模型中有那么多关联。

可以 - 没有规则反对它 - 但这么多规范的问题是你最终会为一个单一目的创建小功能。

最好的系统是 DRY (Don't Repeat Yourself) - 它们一次又一次地重用功能。

例如...

has_one :educator_project_comment

这可以更改为以下内容:

has_many :comments do
   def educators
      where type = "Educator"
   end
end

然后您可以为 Comments 设置一个 STI table setup,一个 type 列表示您正在调用的依赖模型(在本例中为 "Educator" ).