Rails 3 Data Modeling Help - Has Many, Belongs to, Nested Atrributes - ruby-on-rails

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.

Related

Nested forms with multiple table inheritance

How do I build a nested form for objects using multiple table inheritance in rails? I am trying to make a nested form to create an object using a model with a has_many relationship to another set of models that feature multi-table inheritance. I am using formtastic and cocoon for the nested form and the act_as_relation gem to implement the multiple table inheritance.
I have the following models:
class Product < ActiveRecord::Base
acts_as_superclass
belongs_to :store
end
class Book < ActiveRecord::Base
acts_as :product, :as => :producible
end
class Pen < ActiveRecord::Base
acts_as :product, :as => :producible acts_as :product, :as => :producible
end
class Store < ActiveRecord::Base
has_many :products
accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
end'
For this example, the only unique attribute that book has compared to other products is an author field. In reality, I have a number of unique attributes for book which is why I chose multi-table inheritance over the more commonplace single table inheritance.
I am trying to create a nested form that allows you to create a new store with products. Here's my form:
<%= semantic_form_for #store do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<h3>Books/h3>
<div id='books'>
<%= f.semantic_fields_for :books do |book| %>
<%= render 'book_fields', :f => book %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add book', f, :books %>
</div>
<% end %>
<%= f.actions :submit %>
<% end %>
And the book_fields partial:
<div class='nested-fields'>
<%= f.inputs do %>
<%= f.input :author %>
<%= link_to_remove_association "remove book", f %>
<% end %>
</div>
I get this error:
undefined method `new_record?' for nil:NilClass
Based on reading the issues on the github page for act_as_relation, I thought about making the relationship between store and books more explicit:
class Product < ActiveRecord::Base
acts_as_superclass
belongs_to :store
has_one :book
accepts_nested_attributes_for :book, :allow_destroy => true, :reject_if => :all_blank
end
class Book < ActiveRecord::Base
belongs_to :store
acts_as :product, :as => :producible
end
class Store < ActiveRecord::Base
has_many :products
has_many :books, :through => :products
accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
accepts_nested_attributes_for :books, :allow_destroy => true, :reject_if => :all_blank
end
Now, I get a silent error. I can create new stores using the form, and cocoon allows me to add new book fields, but when I submit the store gets created but not the child book. When, I go through the `/books/new' route, I can create a new book record that spans (the products and books table) with no problem.
Is there a workaround to this problem? The rest of the code can be found here.
Maybe you could:
Build the books relation manually on your stores_controller#new action
#store.books.build
Store manually the relation on you stores_controller#create action
#store.books ... (not really confident on how to achieve it)
Keep us posted.
You might want to consider creating your own form object. This is a RailsCast pro video, but here are some of the examples in the ASCIIcast:
def new
#signup_form = SignupForm.new(current_user)
end
This signup form can include relations to your other objects, just as you would in your original controller code:
class SignupForm
# Rails 4: include ActiveModel::Model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
validates_presence_of :username
validates_uniqueness_of :username
validates_format_of :email, with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
validates_length_of :password, minimum: 6
def persisted?
false
end
def subscribed
subscribed_at
end
def subscribed=(checkbox)
subscribed_at = Time.zone.now if checkbox == "1"
end
def generate_token
begin
self.token = SecureRandom.hex
end while User.exists?(token: token)
end
end
Here is the link to the RailsCast. Getting a pro membership might be worth your time. I have been getting lucky with a membership through www.codeschool.com where you can get 'prizes' when you finish courses:
RailsCast:
http://railscasts.com/episodes/416-form-objects

Rails: How do I create a controller for a has_many :through using a join model?

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?

Correct way to create association between jobs, applications and users

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

Restricting a portion of code through an association

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.

Assigning a nested attribute with Formtastic

I've been trying to figure this one out for a while but still no luck. I have a company_relationships table that joins Companies and People, storing an extra field to describe the nature of the relationship called 'corp_credit_id'. I can get the forms working fine to add company_relationships for a Person, but I can't seem to figure out how to set that modifier field when doing so. Any ideas?
More about my project: People have many companies through company_relationships. With that extra field in there I am using it to group all of the specific relationships together. So I can group a person's Doctors, Contractors, etc.
My models:
Company.rb (abridged)
class Company < ActiveRecord::Base
include ApplicationHelper
has_many :company_relationships
has_many :people, :through => :company_relationships
Person.rb (abridged)
class Person < ActiveRecord::Base
include ApplicationHelper
has_many :company_relationships
has_many :companies, :through => :company_relationships
accepts_nested_attributes_for :company_relationships
company_relationship.rb
class CompanyRelationship < ActiveRecord::Base
attr_accessible :company_id, :person_id, :corp_credits_id
belongs_to :company
belongs_to :person
belongs_to :corp_credits
end
My form partial, using formtastic.
<% semantic_form_for #person do |f| %>
<%= f.error_messages %>
<% f.inputs do %>
...
<%= f.input :companies, :as => :check_boxes, :label => "Favorite Coffee Shops", :label_method => :name, :collection => Company.find(:all, :conditions => {:coffee_shop => 't'}, :order => "name ASC"), :required => false %>
So what I would like to do is something like :corp_credit_id => '1' in that input to assign that attribute for Coffee Shop. But formtastic doesn't appear to allow this assignment to happen.
Any ideas on how to do this?
Are you looking for something like
<% semantic_form_for #person do |form| %>
<% form.semantic_fields_for :company_relationships do |cr_f| %>
<%= cr_f.input :corp_credit_id %>
<% end %>
It is in the documentation

Resources