I have 3 Models
class User < ActiveRecord::Base
has_many :teams, :through => :team_memberships
has_many :team_memberships
end
class Teams < ActiveRecord::Base
has_many :users, :through => :team_memberships
has_many :team_memberships
has_many :clubs, :through => :club_memberships
has_many :club_memberships
end
class Clubs < ActiveRecord::Base
has_many :teams, :through => :club_memberships
has_many :club_memberships
end
I want to be able to get a unique list of clubs that the user is a member of. If I have the following:
#teams = User.last.teams
How can I get a list of clubs that these teams are members of. If there are any duplicates I would like to only show them once in the list.
Currently if I do:
<% #user.teams.each do |t| %>
<% t.clubs.each do |c| %>
<%= link_to c.name, c %>
<% end %>
<% end %>
I obviously get a complete list but I want to remove the duplicates. Can anyone offer a fix?
Looks like I can just set up a relationship like so:
class User < ActiveRecord::Base
has_many :clubs, -> { uniq }, :through => :teams
end
and then reference:
<% #user.clubs.each do |c| %>
<%= c.name %>
<% end %>
Please let me know if there is a better way!
Related
I am trying to create two has many relations to the same destination tables but I am unable to override the default method name rails creates when defining the association.
The following is the case:
class User < ApplicationRecord
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :conferences, through: :conference_attendees, class_name: 'attending', dependent: :destroy
has_many :conferences, through: :conference_organizers, source: :conference, dependent: :destroy
class ConferenceOrganizer < ApplicationRecord
alias_attribute :organizers, :users
belongs_to :conference
belongs_to :user
end
class ConferenceAttendee < ApplicationRecord
belongs_to :conference
belongs_to :user
end
class Conference < ApplicationRecord
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :users, through: :conference_attendees, dependent: :destroy
has_many :organizers, through: :conference_organizers, source: :user, dependent: :destroy
I am trying to access all the conferences that a user attended and then all conferences that a user organized using something like following:
User.find(id: <id>).organizing
User.find(id: <id>).attending
but I am unable to and
User.find(id: <id>).conferences
defaults to organizing conferences. How do I access attending conferences?
Since you are doing a join on the same two tables it might be cleaner to do one join table and add a field called status to distinguish organizer from attendee. That way you can add other status values such as "presenter".
set up the table rails g model Registration user:references conference:references status:string; rake db:migrate
Set up the model associations:
# app/models/user.rb
has_many :registrations, dependent: :destroy
# app/models/conference.rb
has_many :registrations, dependent: :destroy
# app/models/registration.rb
belongs_to :user
belongs_to :conference
scope :attendees, -> { where( status: "Attendee") }
scope :organizers, -> { where( status: "Organizer") }
Then to show the attendees and organizers on the conference show page:
# app/controllers/conferences_controller.rb
def show
#conference = Conference.find(params[:id])
#attendee_registrations = #conference.registrations.attendees
#organizer_registrations = #conference.registrations.organizers
end
# app/views/conferences/show.html.erb
<h3>Organizers</h3>
<% #organizer_registrations.each do |registration| %>
<li><%= link_to(registration.user.name, registration.user) %></li>
<% end %>
<h3>Attendees</h3>
<% #attendee_registrations.each do |registration| %>
<li><%= link_to(registration.user.name, registration.user) %></li>
<% end %>
Then to show the conferences on the user's show page:
# app/controllers/users_controller.rb
def show
#user = User.find(params[:id])
#attending_registrations = #user.registrations.attendees
#organizing_registrations = #user.registrations.organizers
end
# app/views/users/show.html.erb
<h3>Conferences Organizing</h3>
<% #organizing_registrations.each do |registration| %>
<li><%= link_to(registration.conference.name, registration.conference) %><br>
Date: <%= registration.conference.date %></li>
<% end %>
<h3>Conferences Attending</h3>
<% #attending_registrations.each do |registration| %>
<li><%= link_to(registration.conference.name, registration.conference) %><br>
Date: <%= registration.conference.date %></li>
<% end %>
If you want two join tables on Users and Conferences you can set it up this way.
# app/models/user.rb
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :attending, through: :conference_attendees, source: :conference
has_many :organizing, through: :conference_organizers, source: :conference
# app/models/conference.rb
has_many :conference_attendees, dependent: :destroy
has_many :conference_organizers, dependent: :destroy
has_many :attendees, through: :conference_attendees, source: :user
has_many :organizers, through: :conference_organizers, source: :user
# app/models/conference_attendee.rb
belongs_to :user
belongs_to :conference
# app/models/conference_organizer.rb
belongs_to :user
belongs_to :conference
Then to show the attendees and organizers on the conference show page:
# app/controllers/conferences_controller.rb
def show
#conference = Conference.find(params[:id])
#attendees = #conference.attendees
#organizers = #conference.organizers
end
# app/views/conferences/show.html.erb
<h3>Organizers</h3>
<% #organizers.each do |user| %>
<li><%= user.name %></li>
<% end %>
<h3>Attendees</h3>
<% #attendees.each do |user| %>
<li><%= user.name %></li>
<% end %>
Then to show the conferences on the user's show page:
# app/controllers/users_controller.rb
def show
#user = User.find(params[:id])
#attending = #user.attending
#organizing = #user.organizing
end
# app/views/users/show.html.erb
<h3>Conferences Organizing</h3>
<% #organizing.each do |conference| %>
<li><%= link_to(conference.name, conference) %><br> Date: <%= conference.date %></li>
<% end %>
<h3>Conferences Attending</h3>
<% #attending.each do |conference| %>
<li><%= link_to(conference.name, conference) %><br> Date: <%= conference.date %></li>
<% end %>
That being said it may be cleaner to use one join table rather than two. I'll put that in a separate answer.
I'm having issue creating a nested polymorphic form. I am following the solution from this problem:
Rails: has_many through with polymorphic association - will this work?
The description was: A Person can have many Events and each Event can have one polymorphic Eventable record
Here are the relevant models:
class Event < ActiveRecord::Base
belongs_to :person
belongs_to :eventable, :polymorphic => true
end
class Meal < ActiveRecord::Base
has_one :event, :as => eventable
end
class Workout < ActiveRecord::Base
has_one :event, :as => eventable
end
class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
end
My controller looks like this:
def
#person = Person.new
#person.meals.new
#person.workouts.new
new
My view looks like this:
<%= form_for #person do |person| %>
<%= person.fields_for :meals, #person.meals.build do |meals| %>
<%= meals.text_field :food %>
<% end %>
<% end %>
The error I am currently getting is:
unknown attribute: person_id
I would think that the polymorphic association is hindering the creation of the object because meals can't be create until person has been created? Did I set up the form correctly? Thanks!
You'll need to include a person_id attribute in the events table (as per the belongs_to association)
There are also some other issues you should resolve:
Controller
def new
#person = Person.build
end
def create
#person = Person.new(person_attributes)
#person.save
end
private
def person_attributes
params.require(:person).permit(:your, :attributes, events_attributes: [:eventable_type, :eventable_id, meal_attributes: [:params], workout_attributes: [:params]])
end
Models
#app/models/person.rb
Class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
def self.build
person = Person.new
person.events.build.build_meal
person.events.build.build_workout
end
end
Views
#app/views/people/new.html.erb
<%= form_for #person do |person| %>
<%= person.fields_for :events do |e| %>
<%= e.fields_for :meal %>
<%= e.text_field :food %>
<% end %>
<% end %>
<% end %>
I have two models "Stores" and "Vendors". I created a separate join model called "Partnerships" so a store can have many vendors and the Vendor can have many stores. When the user is logged in they are affiliated to either a store or vendor. I want them to be able to create partnerships. I think I have the model down based on my research, but I can't seem to get the controller right. Most of the online examples only show the models not the controller.
class Store < ActiveRecord::Base
attr_accessible :industry, :name
has_many :users
has_many :workorders
has_many :locations
has_many :partnerships
has_many :vendors, :through => :partnerships
class Vendor < ActiveRecord::Base
attr_accessible :industry, :name
has_many :users
has_many :workorders
has_many :locations
has_many :partnerships
has_many :stores, :through => :partnerships
class Partnership < ActiveRecord::Base
belongs_to :store
belongs_to :vendor
attr_accessible :store_id, :vendor_id, :store, :vendor
This is my current partnerships_controller#new where I get a type mismatch error during my test.
if params[:store_id]
#store = Store.where(:id => params[:store_id])
#partnership = Partnership.new(store: #store)
else
flash[:error] = "Store partnership required"
end
Here is my new.html.erb for Partnerships:
<% if flash[:error] %>
Not found.
<% else %>
<% if current_user.affiliation == 'Vendor' %>
<div class="page-header">
<h1> <%= #store.name %></h1>
</div>
<% else %>
<div class="page-header">
<h1> <%= #vendor.name %></h1>
</div>
<% end %>
<% end %>
My User model includes an affiliation field that is either "Store" or "Vendor" and also a company_id field.
What would my controller look like to create a new partnership if I am a user whose affiliation = 'Store'? Would I do it in the partnership controller?
I am working on a project involving three models (recipient, award, announcer) and need to have a nested attributes when issuing an award by an announcer to multiple recipients. For an example, award form need to have the ability to do 3 things:
Can add multiple-recipients (i.e. "add recipient", "remove recipient") - nested attributes
After creating a new award, the award will be posted into recipient's profile.
Enables future polling of #recipient.awards and #announcer.awards
Really struggle in terms of how to smartly solve this problem. The following data structure kind of made sense, however can not do "accepts_nested_attributes_for :recipients" in the award form. Can you help? Many thanks in advance.
class Recipient < ActiveRecord::Base
has_many :awards
has_many :announcers, :through => :awards
end
class Announcer < ActiveRecord::Base
has_many :awards
has_many :recipients, :through => :awards
end
class Award < ActiveRecord::Base
belongs_to :announcer
belongs_to :recipient
end
You're just about there. The main issue is that you're trying to create recipient objects in the form rather than just creating a relationship between the award and another object (user). You could do something like this:
class User < ActiveRecord::Base
has_many :recipients
has_many :awards, :through => :recipients
end
# this is your relationship between an award and a user
class Recipient < ActiveRecord::Base
belongs_to :user
belongs_to :award
end
class Award < ActiveRecord::Base
has_many :recipients
has_many :users, :through => :recipients
belongs_to :announcer
accepts_nested_attributes_for :recipients, :allow_destroy => true
end
class Announcer < ActiveRecord::Base
has_many :awards
has_many :recipients, :through => :awards
end
Then you would just do a nested form that would build the recipients_attributes array:
<%= form_for #award do |f| %>
<%= f.text_field :name %>
<div id="recipients">
<% #award.recipients.each do |recipient| %>
<%= render :partial => '/recipients/new', :locals => {:recipient => recipient, :f => f} %>
<% end %>
</div>
<%= link_to_function 'add recipient', "jQuery('#recipients').append(#{render(:partial => '/recipients/new').to_json})" %>
<% end %>
And, to keep it DRY just push the nested part into a partial:
# app/views/recipients/_new.html.erb
<% recipient ||= Recipient.new %>
<%= f.fields_for 'recipients_attributes[]', recipient do |rf| %>
<%= rf.select :user_id, User.all %>
<%= fr.check_box '_delete' %>
<%= fr.label '_delete', 'remove' %>
<% end %>
Obviously the User.all call isn't ideal so maybe make that an autocomplete.
models/user.rb
class User < ActiveRecord::Base
has_many :clubs, :dependent => :destroy
has_many :announcements, :dependent => :destroy
end
models/announcement.rb
class Announcement < ActiveRecord::Base
belongs_to :user
belongs_to :club
end
models/club.rb
class Club < ActiveRecord::Base
belongs_to :user
has_many :announcements, :dependent => :destroy
end
controllers/announcements/announcements_controller.rb
def index
#announcements = Announcement.find(:all, :include => [:user, :club])
end
Problem:
When i type this code,
views/announcements/index.html.erb
<% #announcements.each do |announcement| %>
<%= announcement.user.username %>
<% end %>
I get this error:
NoMethodError in Announcements#index
undefined method `username' for nil:NilClass
when i change code to this, it works.
<% #announcements.each do |announcement| %>
<%= announcement.club.user.username %>
<% end %>
Why is the first code is not working? What is the difference between these codes. Thanks.
It looks like you've got an announcement which has no user set, but does have a club which in turn does have a user. Perhaps consider adding a validation to require that the :user_id column on announcement is present.