Hey I hope you can help me here.
I got a Role model:
has_many :users, :through => :role_assignments
has_many :role_assignments
a role assignment model:
belongs_to :user
belongs_to :role
and a user model:
has_many :roles, :through => :role_assignments
has_many :role_assignments
I want to display the users roles in a view.
I tried some stuff like: user.roles.names but it didnt work
Since user.roles is an collection (array) of roles, you can't call names directly. Now I assume that the attribute you want to access is name so in that case you could do:
user.roles.map(&:name).join(", ")
which will collect all the names from the roles and then join them into a string seperated by commas. That is very simple and not very flexible. If you instead want to style it in some way you could do like this:
<% user.roles.each do |role| %>
<p>Role: <%= role.name %></p>
<% end %>
Related
The models I have:
Category:
class Category < ApplicationRecord
has_many :categorizations
has_many :providers, through: :categorizations
accepts_nested_attributes_for :categorizations
end
Provider:
class Provider < ApplicationRecord
has_many :categorizations
has_many :categories, through: :categorizations
accepts_nested_attributes_for :categorizations
end
Categorization:
class Categorization < ApplicationRecord
belongs_to :category
belongs_to :provider
has_many :games, dependent: :destroy
accepts_nested_attributes_for :games
end
Game:
class Game < ApplicationRecord
belongs_to :categorization
end
I need to display the games, that belongs to a specific provider. I tried to do it like:
<% #provider.categorizations.joins(:games).each do |game| %>
<%= game.title %>
<% end %>
It gives me an error: NoMethodError: undefined method 'title' for #<Categorization:0x007f2cf6ee49e8>. So, it loops through the Categorization. What is the best way to loop through the joined games table? Thanks.
First, you should do the request in your controller, or even better call a scope (defined in a model) from the controller.
Do not forget that Active Record is just an ORM, a tool allowing you to manipulate SQL.
With #provider.categorizations.joins(:games) you are not asking for games. You are asking for the categorizations and you do a JOIN with the games table. This joins is usually to allow to filter by games attributes.
To do what you want you should do the following :
#games = Game.joins(:categorization).where('categorization.provider_id = ?',#provider.id)
As you can see, the join do not return categorization, it allow me to use categorization as a filter.
You should always be aware of the SQL generated by Active Record. Look at the SQL query generated in your server's traces.
I'm guessing 'title' is an attribute of games and not categorization, so you either need to return an array of games, or add a select on the end to pull the title attribute into the categorization object, like so:
<% #provider.categorizations.joins(:games).select('dba.games.title').each do |game| %>
<%= game.title %>
<% end %>
Just to add- you shouldn't really be doing this in the view file. I'd go as far as not even doing this in the controller. I tend to encapsulate this sort of logic into a service class, which is instantiated in the controller to return a set of results. The controller should only be passing the result set on, which is then presented by the view.
class Provider < ActiveRecrord::Base
# this could be a scope instead, or in a seperate class which
# the provider model delegates to- whatever floats you boat
def get_games
# you could use pluck instead, which would return an array of titles
categorizations.joins(:games).select('dba.games.title')
end
end
class ProviderController < ApplicationController
def show
provider = Provide.find(params[:id])
#games = provider.get_games
end
end
<% #games.each do |game| %>
<%= game.title %>
<% end %>
I want my users to have many skills. I do have a users and skills database table.
I used has_many_and_belongs_to association in user.rb
has_many :skills
which I am not sure if its correct. And in skill.rb
has_and_belongs_to_many :users
I also created a migration like that:
def change
create_table :user_skills do |t|
t.belongs_to :users
t.belongs_to :skills
end
Is this correct?
So IF this is correct, how do I add new skills to my user? What is the general approach?
What I thought of,
In my users controller on update action I will be updating user's skill and update the user_skills table.
How is this done?
Also How do I iterate through my user_skills table for a specific user? (in view)
Any guidance, resource, tip will be great help for me as its the first time i do something like this in Rails.
Thanks
In Rails, most would prefer to use has_many :through over habtm associations. Here's a guide on how to use it: ActiveRecord guide.
A has_many through association for users and skills would look like this in your relevant models:
class User < ActiveRecord::Base
has_many :user_skills
has_many :skills, through: :user_skills
end
class UserSkill < ActiveRecord::Base
belongs_to :user
belongs_to :skill
end
class Skill < ActiveRecord::Base
has_many :user_skills
has_many :users, through: :user_skills
end
Your migration would look like:
def change
create_table :user_skills do |t|
t.references :user, index: true
t.references :skill, index: true
end
end
The indexes in the migration are for faster look-ups for using the reference_id. It's advisable to do that for all references.
To add new skills to your user, you can refer to this SO answer.
To update a user's skill, you could do this:
#skill = #user.skills.find(params[:skill_id])
#skill.update(skill_params)
To create a user's skill, you could do this:
#user.skills.create(skill_params)
To add a skill to user, you could do this in your update action:
#user.update(user_params)
#app/views/users/edit.html.erb
<%= f.select :skill_ids, Skill.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>
When working with has_many through, you won't need to go through the user_skills table to get a specific user. You would, however, might need to get a specific user from a skill. To do this:
#skill.users.find(user_id)
Hope that helps!
If you set user to have_and_belong_to_many :skills also then this will work.
To create a new skill for a user do
user.skills.create!{...}
or to associate an existing skill with a user do
user << skill
"In my users controller on update action I will be updating user's skill and update the user_skills table. How is this done?"
user = User.find params[:id]
skills = user.skills
You can then do what you like to users skills
"Also How do I iterate through my user_skills table for a specific user? (in view)"
user.skills.each do |skill|
...
end
for more on HABTM association see http://guides.rubyonrails.org/association_basics.html#has-and-belongs-to-many-association-reference
Forgive me If I get it wrong, try to fill in the gaps but I think you want something that looks like this.
controller
def index
#to fetch all skills associated to users (add where u.id=? to fetch for a single user)
#users = User.select("u.name, s.name").
from("users u, skills s, users_skills us").
where("u.id = us.user_id").
where("s.id = us.skill_id")
end
def new
#user = User.new
#skills = Skill.all
end
def create
#user = User.new(params[:user])
...............................
end
in the create form
<%= form_for #user do |f| %>
<%= f.collection_select(:skill_ids, #skills,:id,:name)%>
<%= f.submit "Save" %>
<% end %>
In order to use HABTM you need a join table named either users_skills or skills_users (not sure it matters). It should contain two integer columns named user_id and skill_id. You should create indices for them as well. In your User model you want has_and_belongs_to_many :skills and in your Skill model you want has_and_belongs_to_many :users.
You need has_and_belongs_to_many on both sides of the realtionship.
class User
has_and_belongs_to_many :skills
class Skill
has_and_belongs_to_many :users
Alternatively (and better, in my opinion) would be to use has_many :through:
class User
has_many :user_skills
has_many :skills, through: :user_skills
class Skill
has_many :user_skills
has_many :users, through: :user_skills
I am new to rails, and am trying to set up a many-to-many relationship in my rails project. I have a small strategy, but I am not sure if its the correct way.
Aim:
I have a table of users, and a table of groups. Users can be part of many groups, and each group may have many users.
Strategy:
Set up User migration to have name:string
Set up Group migration to have name:string
Set up a Join table migration
Set up User model such that it would have has_and_belongs_to_many :groups
Set up Group model such that it would have has_and_belongs_to_many :users
Would this be the correct strategy? Thanks!
Railcast Summary from answer:
For those that are interested - Railcast suggests you to use a has_many :through association since the strategy above has the limitation that you cannot add extra relation-specific information.
check out: http://kconrails.com/tag/has_many/
First, I assume, you have a user-model with a field "name" and a group-model with a field "name".
You need a model between users and groups. Let's call it grouping:
rails g model grouping user_name:string group_name:string
In the grouping-model (grouping.rb), you put:
belongs_to :user
belongs_to :group
In the user-model:
has_many :groupings, :dependent => :destroy
has_many :groups, :through => :groupings
And in the group-model:
has_many :groupings, :dependent => :destroy
has_many :users, :through => :groupings
In the _form file to edit or update a user's profile, you put:
<div class="field">
<%= f.label :group_names, "Groups" %>
<%= f.text_field :group_names %>
</div>
And, finally, the User-class must know, what to do with the information from the form. Insert into user.rb:
attr_writer :group_names
after_save :assign_groups
def group_names
#group_names || groups.map(&:name).join(' ')
end
private
def assign_groups
if #group_names
self.groups = #group_names.split(/\,/).map do |name|
if name[0..0]==" "
name=name.strip
end
name=name.downcase
Group.find_or_create_by_name(name)
end
end
end
assign_groups removes whitespace and downcases all words, so you won't have redundant tags.
Now, you can show the groups for a user in the show file of his or her profile:
<p>Groups:
<% #user.groups.each do |group|%>
<%= group.name %>
<% end %>
</p>
Hope, that helps.
I have Four models.
Product
Category
Categorization
Images
Product.rb
has_many :images
has_many :categorizations
has_many :categories, :through => :categorizations
Category.rb
has_many :categorizations
has_many :products, :through => :categorizations
Categorization.rb
belongs_to :category
belongs_to :product
Image.rb
belongs_to :product
When a user clicks to see a product, I want to have a section on the bottom that shows images similar products (based on same category/categories).
I guess my problem is that there are so many nested relationships that I don't know how to extract the relationships out.
Any help is appreciated. Thanks.
Just think of what object you are currently working with and what object you need to access. In this case I assume you set up #product in the controller, and it seems that you want a collection #similar_products that you then loop through on the bottom of the page.
We have a product to work with at the beginning. From this, we want products that are in the same categories as the product. So, in your controller do
#similar_products = #product.categories.inject({}) do |result_hash, category|
result_hash[category.name.to_sym] => category.products.reject {|p| p == #product}
result_hash
end
So what we end up with is something that looks like this:
{:category_1 => [product1, product2, product3], :category_2 => [product4,product5]...}
I might recommend limiting the number of products for each category to something like 5 by changing the result_hash assignment to
result_hash[category.name.to_sym] => category.products[0..4]
Now in your view you can loop through the products by category if you like:
<% #similar_products.each do |k,product_array| # remember that the key is the name and the value is an array %>
<% product_array.each do |product| %>
<img src="<%= product.image.path %>" />
<% end %>
<% end %>
I have a model called Kase each "Case" is assigned to a contact person via the following code:
class Kase < ActiveRecord::Base
validates_presence_of :jobno
has_many :notes, :order => "created_at DESC"
belongs_to :company # foreign key: company_id
belongs_to :person # foreign key in join table
belongs_to :surveyor,
:class_name => "Company",
:foreign_key => "appointedsurveyor_id"
belongs_to :surveyorperson,
:class_name => "Person",
:foreign_key => "surveyorperson_id"
I was wondering if it is possible to list on the contacts page all of the kases that that person is associated with.
I assume I need to use the find command within the Person model? Maybe something like the following?
def index
#kases = Person.Kase.find(:person_id)
or am I completely misunderstanding everything again?
Thanks,
Danny
EDIT:
If I use:
#kases= #person.kases
I can successfully do the following:
<% if #person.kases.empty? %>
No Cases Found
<% end %>
<% if #person.kases %>
This person has a case assigned to them
<% end %>
but how do I output the "jobref" field from the kase table for each record found?
Maybe following will work:
#person.kase.conditions({:person_id => some_id})
where some_id is an integer value.
Edit
you have association so you can use directly as follows:
#kases= #person.kases
in your show.rhtml you can use instance variable directly in your .rhtml
also #kases is an array so you have to iterate over it.
<% if #kases.blank? %>
No Kase found.
<% else %>
<% for kase in #kases %>
<%=h kase.jobref %>
<% end %>
<% end %>
If your person model has the association has_many :kases then, you can get all the cases that belongs to a person using this
#kases = Person.find(person_id).kases
Assuming person_id has the id of the person that you want to see the cases for.
You would probably want something like
has_many :kases
in your Person model, which lets you do #kases = Person.find(person_id).kases
as well as everything else that has_many enables.
An alternate method would be to go through Kase directly:
#kases = Kase.find_all_by_person_id(person_id)