Rails - how to show attribute of an associated model

后端 未结 2 737
臣服心动
臣服心动 2020-12-22 00:58

I am trying to make an app in Rails 4.

I just asked this related question and got a clear answer. It seems I can\'t understand how to take that logic and apply it e

相关标签:
2条回答
  • 2020-12-22 01:36

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

    I don't think you appreciate how ActiveRecord associations work in Rails. I'll explain further down the page.


    Your associations will be the likely cause of the problem.

    Setting up complicated associations is always tricky - it's best to keep the data as separate as possible.

    Here's how I'd construct the models / associations:

    #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
    

    This allows you to do the following:

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

    Because the associations actually associate your data, you'll be able to do the following:

    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 %>
    

    My code above allows for multiple universities. Thinking about it, it should be limited to one, but I'll leave it as is for now, maybe change it later.

    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).

    Don't use two logo method, it's an antipattern (explained below)


    The fix for this is two-fold:

    Firstly, make sure you're calling @creator_profile.university with the following:

    <%= @creator_profile.university %> 
    

    If this works, it means you have a problem with .logo.logo (detailed below), if it doesn't, it means you've not defined @creator_profile or the university association correctly.

    Secondly, you need to ensure you have the correct controller/view setup.

    The problem for many people - especially beginners - is they simply don't understand the way Rails works with controllers & views. You need to appreciate that each time you render a view, the only data it has access to is that which you define in the corresponding controller action...

    #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" %>
    

    Trivia

    1. @project.creator_id = current_user.id

    This should not have to be defined.

    You should be able to change the foreign_key in the association, so that Rails will automagically define the creator_id for you:

    #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

    This is an antipattern.

    Calling the same method twice is simply bad practice - why are you doing it?

    You either want to delegate any recursive data you're trying to access (such as the example with .creator_profile above), or you'll want to restructure that functionality.

    You want the following:

    If you have to delegate to an assets model, you could get away with the following:

    <%= @creator_profile.university.images.logo %>
    
    0 讨论(0)
  • 2020-12-22 01:46

    I think that you need to understand some basic concepts of Ruby and Ruby and Rails to solve this question yourself.

    In ruby, vars with @ are instance variables and are available all over the class. That means that they will be available in your view if you declare them in your controller.

    EG @creator_profile = @profile.user

    On the other hand, vars without @ are only available inside the same block.

    An example:

    #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 about ruby vars and scopes.

    Also, I think that you are relying too much on 'rails magic' (or you are skipping some code lines), if you don't declare an instance var, it won't exist. Naming conventions don't work that way.

    At last but not at least, having a look at your relations, I think that they need some refactor. Also the use of singular and plural is not correct. I know that it's not real code but it denotes that they don't reflect real relationships between entities.

    Don't try to make 'octopus' models, where everybody belongs to everybody, and think about the relationships itself, not only trying to associate models. EG:

    Profile
    belongs_to :creator, class_name: 'User'
    

    This way you can write:

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

    You will understand better what you are doing.

    Hope it helps.

    0 讨论(0)
提交回复
热议问题