Rails - 如何显示关联模型的属性

Rails - how to show attribute of an associated model

我正在尝试在 Rails 4 中制作一个应用程序。

我刚问了这个相关问题,得到了明确的答复。看来我无法理解如何采用该逻辑并将其应用到其他地方。

我有一个用户模型、一个配置文件模型、一个项目模型和一个大学模型。

协会是:

Profile belongs to university
Profile belongs to user
University has many profiles
University has many projects
Projects HABTM user
Projects belong to universities

在我的项目控制器中,我定义@creator如下:

def create
    logger.debug "xxx create project"
    #authorise @project
    @project = Project.new(project_params)
    @project.creator_id = current_user.id
    @project.users << current_user
    respond_to do |format|
      if @project.save

        format.html { redirect_to @project }
        format.json { render action: 'show', status: :created, location: @project }
      else
        format.html { render action: 'new' }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

我尝试这样定义 creator_profile:

def show
    #authorise @project

    @project = Project.find(params[:id])
    @creator = User.find(@project.creator_id)
    @creator_profile = @creator.profile

  end

在我的 uni table 中,我有名为 logo 和 name 的属性。我使用定义了徽标的头像上传器(这就是为什么我在下面有两个 .logo 的原因)。

在我的项目中,show,我想显示项目创建者所属的大学。

我试过这个:

<%= image_tag(@creator_profile.university.logo.logo) %> 
        <div class="generaltext"><%= @creator_profile.university.name %> </div>

我得到这个结果:nil:NilClass

的未定义方法“徽标”

基于link我上面的问题

<%= image_tag(creator_profile.university.logo.logo) %> 
            <div class="generaltext"><%= creator_profile.university.name %> </div>

我得到这个结果:

undefined local variable or method `creator_profile' for #<#<Class:0x007f998f17ad88>:0x007f998d1ce318>

我不确定我是否理解我在上一个问题的答案中给出的非常详细的解释。如果第一个版本是正确的,那么我根本不明白这个解释。如果第二个版本是正确的,那么为什么会出现这个错误信息?

我想知道问题是否出在大学和用户之间没有关联?我希望根据创建项目的用户,找到创建者所属的uni。

这就是我尝试的原因:

<%= image_tag(creator_profile.project.university.logo.logo) %> 
                <div class="generaltext"><%= creator_profile.project.university.name %> </div>

我收到这个错误:

undefined method `project' for #<Profile:0x007f998ada41b8>

我认为你需要了解Ruby和Ruby和Rails的一些基本概念才能自己解决这个问题。

在ruby中,带有@的变量是实例变量并且在class中都可用。这意味着如果您在控制器中声明它们,它们将在您的视图中可用。

EG @creator_profile = @profile.user

另一方面,没有@的变量只能在同一块内使用。

一个例子:

#controller
@users = User.all #@users, instance variable

#view
<% @users.each do |user| %>
  <h3><%= user.name  %></h3> #user, local variable. This will work
<% end %>
<h3><%= user.name %></h3> #this won't work because it is outside the block

Google 关于 ruby 变量和作用域。

此外,我认为您过分依赖 'rails magic'(或者您跳过了一些代码行),如果您不声明一个实例变量,它就不会存在。命名约定不是那样工作的。

最后但并非至少,看看你的关系,我认为他们需要一些重构。单数和复数的使用也不正确。我知道这不是真正的代码,但它表示它们不反映实体之间的真实关系。

不要尝试制作 'octopus' 模型,每个人都属于每个人,并考虑关系本身,而不仅仅是尝试关联模型。例如:

Profile
belongs_to :creator, class_name: 'User'

这样你可以写:

#controller
@profile_creator = Profile.find(params[:id]).creator

#view
@profile_creator.university

你会更好地理解你在做什么。

希望对您有所帮助。

It seems I can't understand how to take that logic and apply it elsewhere.

我认为您不了解 ActiveRecord 关联在 Rails 中的工作方式。我会在页面下方进一步解释。


您的关联可能是问题的原因。

设置复杂的关联总是很棘手 - 最好将数据尽可能分开。

以下是我构建模型/关联的方式:

#app/models/university_student.rb
class UniversityStudent < ActiveRecord::Base
   belongs_to :university
   belongs_to :student, class_name: "User" #-> student_id
end

#app/models/user.rb
class User < ActiveRecord::Base
   has_many :placements, class_name: "UniversityStudent", foreign_key: :student_id #-> user.placements
   has_many :universities, through: :placements #-> user.universities

   has_and_belongs_to_many :projects #-> user.projects

   has_one :profile #-> user.profile (avatar etc)

   has_many :created_projects, class_name: "Project", foreign_key: :creator_id
end

#app/models/profile.rb
class Profile < ActiveRecord::Base
   belongs_to :user #-> store avatar here. This can be used across entire app
end 

#app/models/university.rb
class University < ActiveRecord::Base
   has_many :projects
   has_many :students, class_name: "UniversityStudent" #-> university.students
end

#app/models/project.rb
class Project < ActiveRecord::Base
   belongs_to :university
   belongs_to :creator, class_name: "User" #-> creator_id

   has_and_belongs_to_many :users

   delegate :profile, to: :creator, prefix: true #-> @project.creator_profile
end

这允许您执行以下操作:

def create
   @project = curent_user.created_projects.new project_params
   @project.users << current_user

因为关联实际上关联您的数据,您将能够执行以下操作:

def show
    @project = Project.find params[:id]
    #@creator_profile = @project.creator.profile
    @creator_profile = @project.creator_profile #-> if you use the delegate method outlined in the models
end

--

In my projects, show, I want to display the university that the project creator belongs to.

#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
   def show
      #@project = Project.find params[:id]
      @project = current_user.created_projects.find params[:id]
   end
end

#app/views/projects/show.html.erb
<%= @project.creator.universities.first %>

我上面的代码允许多所大学。想了想,应该限制在一个,不过暂时先这样吧,等会再改吧。

In my uni table, I have attributes called logo and name. I use avatar uploader in which i have logo defined (that's why I have two .logo below).

不要使用两个 logo 方法,这是一个反模式(下面解释)


此问题的修复有两个方面:

首先,请确保您使用以下方式调用 @creator_profile.university

<%= @creator_profile.university %> 

如果有效,则说明您的 .logo.logo 有问题(详见下文),如果无效,则说明您没有定义 @creator_profileuniversity 关联正确。

其次,您需要确保您的 controller/view 设置正确。

许多人(尤其是初学者)的问题是他们根本不理解 Rails 使用控制器和视图的方式。您需要了解每次呈现视图时,它可以访问的 数据是您在相应的 controller 操作中定义的数据...

#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
    def show
        @project = Project.find params[:id]
        @creator_profile = @project.creator_profile
    end
end

#app/views/projects/show.html.erb
<%= content_tag :div, @creator_profile.universities.first.name, class: "generaltext" %>

知识问答

  1. @project.creator_id = current_user.id

这不应该被定义。

您应该可以更改关联中的 foreign_key,以便 Rails 自动为您定义 creator_id

#app/models/project.rb
class Project < ActiveRecord::Base
    belongs_to :creator, class: "User" #-> foreign_key should be :creator_id
end

#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
   def create
      @project = current_user.created_projects.new project_params #-> populates foreign key automatically.

--

  1. .logo.logo

这是一个antipattern

调用同一个方法两次简直是不好的做法 - 你为什么要这样做?

您要么想要委托任何您尝试访问的 recursive data(例如上面 .creator_profile 的示例),要么您想要重组该功能。

您想要以下内容:

如果您必须委托给资产模型,您可以使用以下方法:

<%= @creator_profile.university.images.logo %>