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
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 theproject 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
@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.
--
.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 %>
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.