I have built a ruby on rails app that lets users track their workouts. User has_many workouts. In addition, a User can create a box (gym) if they are a gym owner. Users can then associate with that box through a Membership resource.
I create that association for the current_user with a remote form in /views/boxes/show.html.erb:
<% remote_form_for Membership.new do |f| %>
<%= f.hidden_field :box_id, :value => #box.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= submit_tag "I am a member of this box" , :class => '' %>
<% end %>
I now want to give the current_user the ability to remove their association. How do I structure the link/form and what should the memberships_controller destroy action look like?
Below is a list of my associations if it helps:
User
class User < ActiveRecord::Base
has_many :boxes
has_many :workouts, :dependent => :destroy
end
Workout
class Workout < ActiveRecord::Base
belongs_to :user
belongs_to :box
end
Box
class Box < ActiveRecord::Base
belongs_to :user
has_many :users, :through => :memberships
has_many :workouts, :through => :users
has_many :memberships
end
Membership
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :box
end
After you have done the changes suggested here, and assuming that you have obtained the membership for the current_user and the box using the following code,
#membership = current_user.memberships.for_box(box).first
create a link for the user to delete the membership as follows.
link_to 'Delete Membership', membership_path(#membership), :method => :delete
Membership controller should have a destroy method that looks like the following.
def destroy
#membership = Membership.find(params[:id])
#membership.destroy
end
This is assuming that your memberships controller is restful and you have map.resources :memberships in your routes.
Related
I'm having some trouble to implement a nested form with a has_one :through association.
Models
# model: member.rb
belongs_to :user
has_one :academic
# model: user.rb
has_one :member
has_one :academic, through: :member
accepts_nested_attributes_for :member, reject_if: :all_blank
accepts_nested_attributes_for :academic, reject_if: :all_blank
# model: academic.rb
belongs_to :member
belongs_to :user
Controller
# users_controller.rb
def new
#user = User.new
#user.build_member
#user.build_academic
end
I also have tried with:
#user.member.build_academic
View
# new.html.erb
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |ff| %>
<%= ff.text_field :email %>
# member belongs to user so I can call a fields_for
<% ff.fields_for :member do |f| %>
<%= f.text_field :name %>
# this part is not shown. What is wrong with my association?
<% f.fields_for :academic do |a| %>
<%= a.text_field :major %>
<% end %>
<% end %>
<% end %>
I've taken a look into the Rails documentation. The first fields_for is shown in the page (:member), but the second one (:academic), which has the has_one :through association, is not shown in the page.
Any help will be appreciated. Thank you.
Through
If you want to build your data through a relation, you have to pass the associated data as it's constructed:
#app/models/member.rb
class Member < ActiveRecord::Base
belongs_to :user
has_one :academic
accepts_nested_attributes_for :academic
end
#app/models/user.rb
class User < ActiveRecord::Base
has_one :member
has_one :academic, through: :member
accepts_nested_attributes_for :member
end
#app/models/academic.rb
class Academic < ActiveRecord::Base
belongs_to :member
belongs_to :user
end
This will allow you to do the following:
#app/controllers/members_controller.rb
class MembersController < ApplicationController
def new
#member = Member.new
#member.build_member.build_academic
end
def create
#member = Member.new member_params
#member.save
end
private
def member_params
params.require(:member).permit(:x, :y, :z, academic_attributes: [:some, :attributes, member_attributes:[...]])
end
end
This would permit the following:
#app/views/users/new.html.erb
<%= form_for #user do |f| %>
<%= f.fields_for :member do |m| %>
<%= f.text_field :name %>
...
<%= m.fields_for :academic do |a| %>
<% a.text_field :name %>
...
<% end %>
<% end %>
<%= f.submit %>
<% end %>
This works to build a new member object, and a new academic object from the user. Although not strictly what you're asking, it looks like it could benefit you in some form.
Associations
If you want to do what you're asking (IE build_member and build_academic exclusively), you'll need to get rid of the has_one :through relationship...
#app/models/user.rb
class User < ActiveRecord::Base
has_many :memberships
has_many :academics, through: :memberships
end
#app/models/membership.rb
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :academic
end
#app/models/academic.rb
class Academic < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
The problem is you're basically trying to build a relationship for a direct association (member) and an indirect relationship (academic).
If you want to build both exclusively, you have to make them have a direct association with your main model. The above should allow you to do the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#user.members.build.academics.build
end
end
Much like my top example, this will pass nested data through your form - if you wanted to have it completely exclusively, do this:
#app/models/user.rb
class User < ActiveRecord::Base
has_one :member
has_one :academic
has_and_belongs_to_many :academics
end
#app/models/member.rb
class Member < ActiveRecord::Base
belongs_to :user
end
#app/models/academic.rb
class Academic < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :users
end
This will allow the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#user.build_member
#user.build_academic
end
def create
#user = User.new user_params
#user.save
end
private
def user_params
params.require(:user).permit(:user, :params, member_attributes: [], academic_attributes:[])
end
end
For future reference or for anyone that has the same problem, I found a way around to fix this problem, in case you have same structure.
I could fix this by going to the view, before the form, and writing:
<% resource.member.build_academic %>
In my case resource is User, set by devise, a Rails gem used for authentication.
And you don't need to reference any :through or whatsoever in your model.
It is not the most efficient way, but I haven't found any other solution. Hope it helps.
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 creating an app that allows users to create and apply for jobs.
The issue I am having is in getting the associations correct between my three models.
Currently I have the following:
class App < ActiveRecord::Base
belongs_to :job
belongs_to :user
end
class Job < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :apps, :through => :users
end
class User < ActiveRecord::Base
has_many :jobs
has_many :apps, :through => :jobs
end
In my database table for Apps I have two additional columns for user_id and job_id so that the association can be made correctly there.
I am also unsure how I would create a form for say a new application. Currently I have used the following but because I don't have apps as a nested resource of users I am unsure if this is what's causing the issues:
class AppsController < ApplicationController
def new
#user = current_user
#app = #user.apps.build
end
def create
#user = current_user
#app = #user.apps.create(params[:app])
if #app.save
redirect_to user_path
else
render new_app_path
end
end
and
<%= form_for [#app] do |f| %>
<div class = "field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class = "field">
<%= f.label :cover_letter %>
<%= f.text_field :cover_letter %>
</div>
<div class = "field">
<%= f.label :cv %>
<%= f.text_field :cv %>
</div>
<%= f.submit "Submit" %>
<% end %>
It would be great if someone could provide an example of how they would setup the associations for this app and how they would ensure that the related forms worked with this setup.
Thanks in advance for your help!
I have also pushed my app to Github in case that helps anyone: Github Link
I think there will be relationship many-to-many between users and jobs.And applications can act as join table (as jobs_users).
so models ...
class App < ActiveRecord::Base
belongs_to :job
belongs_to :user
end
class Job < ActiveRecord::Base
has_many :users
has_many :apps, :through => :apps
end
class User < ActiveRecord::Base
has_many :jobs,:dependent => :destroy
has_many :apps, :through => :apps
end
And for nested form go through this
http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast
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.
I have built a ruby on rails app that lets users track their workouts. User has_many workouts. In addition, a User can create a box (gym) if they are a gym owner. The purpose is to filter activity of users such that they can only see information related to their gym. Users can then specify if they are a member of that box through a Membership model. The Membership table collects #box.id and current_user.id in the membership.box_id and user.id columns respectively.
The user associates through the following form in the /views/boxes/show.html.erb view:
<% remote_form_for Membership.new do |f| %>
<%= f.hidden_field :box_id, :value => #box.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= submit_tag "I am a member of this box" , :class => '' %>
<% end %>
I then display, in the box show page all the users who are members of that box.
<% #box.users.each do |user| %>
<%= link_to (user.username), user %><br/>
<% end %>
I am trying to restrict the form to only users who are not already members of that box but I am not sure how to write the <% unless ... %> statement.
Here are the rest of the associations:
User
class User < ActiveRecord::Base
has_many :boxes
has_many :workouts, :dependent => :destroy
end
Workout
class Workout < ActiveRecord::Base
belongs_to :user
belongs_to :box
end
Box
class Box < ActiveRecord::Base
belongs_to :user
has_many :users, :through => :memberships
has_many :workouts, :through => :users
has_many :memberships
end
Membership
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :box
end
# Membership model
named_scope :for_box, lambda {|box| {:conditions => {:box_id => box.id}}}
# User model
has_many :memberships
def member_of_box?(box)
!memberships.for_box(box).blank?
end
# View
<% unless current_user.member_of_box?(box) %>
# Show the form
<% end %>
Disclaimer: The code is not tested. It might need minor alterations.