Help with rails belongs_to - ruby-on-rails

I'm stuck -
I'm building an accounting app in rails, and have a model where a customer will have multiple invoices as well as multiple payments (related to those invoices)
Here's a quick look at a simplified model:
class Customer < ActiveRecord::Base
has_many :customer_payments
has_many :invoices
accepts_nested_attributes_for :customer_payments, :allow_destroy => true
end
class CustomerPayment < ActiveRecord::Base
has_many :customer_payment_items
belongs_to :customer
belongs_to :invoice
accepts_nested_attributes_for :customer_payment_items
end
class CustomerPaymentItem < ActiveRecord::Base
belongs_to :invoice, :inverse_of => :customer_payment_items
belongs_to :customer_payment
end
class Invoice < ActiveRecord::Base
has_many :invoice_lines, :dependent => :destroy
has_many :customer_payment_items, :inverse_of => :invoice
belongs_to :customer
accepts_nested_attributes_for :invoice_lines, :allow_destroy => true
end
I have a nested form where I want to show Customer attributes, CustomerPayment attributes and CustomerPaymentItem attributes - which all works fine.
I also want to show Invoice attributes for each CustomerPaymentItem (each CustomerPaymentItem relates back to a single invoice) and while I can get a form to show the CustomerPaymentItem info, I can't get it to show Invoice information which is needed to give reference to the user. - I'm having trouble getting data from a belongs_to association to show on the form.
I'm at a loss - Shouldn't I be able to traverse the belongs_to association? FYI - I can send data to the log where I know the invoice data is populated during the CustomerPayment.new call, it seems to just get lost between the controller and the form.
How should I access that data? Here's the form info -- (coming from a couple of rendered forms) the stuff that doesn't show is in between the ---.
<p>
<%= f.label :amount %><br />
<%= f.text_field :amount %>
</p>
<p>
<%= f.label :invoice_id %><br />
<%= f.text_field :invoice_id %>
</p>
<p>
<% f.fields_for :invoice do |builder| %>
--- doesn't show
Invoice Detail
<p>
<%= builder.label :number %><br />
<%= builder.text_field :number %>
</p>
<p>
<%= builder.label :sub_total %><br />
<%= builder.text_field :sub_total %>
</p>
--- doesn't show
<% end %>
</p>
Am I missing something in my fields_for to show the :invoice reference data? Is my model too complex for rails to make sense of?

#corroded was right - the issue was my lack of an = sign in
<% f.fields_for :invoice do |builder| %>
I guess everyone needs to learn that lesson

Related

Rails has_many :through nested forms with simple form

I am trying to make a player character generator. I have a form that hopefully will allow me to attach skills with their values to a character sheet model. I made models like this:
class CharacterSheet < ApplicationRecord
has_many :character_sheet_skills, dependent: :destroy
has_many :skills, through: :character_sheet_skills
belongs_to :user
accepts_nested_attributes_for :skills
end
class Skill < ApplicationRecord
has_many :character_sheet_skills, dependent: :destroy
has_many :character_sheets, through: :character_sheet_skills
attr_reader :value
end
class CharacterSheetSkill < ApplicationRecord
belongs_to :skill
belongs_to :character_sheet
end
Character sheet model holds data about player character and skill model has all skills available in game. In CharacterSheetSkill I'd like to store the skills that the player chooses for his character together with an integer field setting the skill value.
When opening form, I already have a full list of skills in database. All I want to do in form is create a character sheet that has all of these skills with added value. I tried using "fields_for" in form, but I couldn't really get that to work. Right now it looks like this:
<%= simple_form_for [#user, #sheet] do |f| %>
<%= f.input :name %>
<%= f.input :experience, readonly: true, input_html: {'data-target': 'new-character-sheet.exp', class: 'bg-transparent'} %>
...
<%= f.simple_fields_for :skills do |s| %>
<%= s.input :name %>
<%= s.input :value %>
<% end %>
<% end %>
How can I make that form so it saves character sheet together with CharacterSheetSkills?
A better idea here is to use skills as a normalization table where you store the "master" definition of a skill such as the name and the description.
class CharacterSheetSkill < ApplicationRecord
belongs_to :skill
belongs_to :character_sheet
delegate :name, to: :skill
end
You then use fields_for :character_sheet_skills to create rows on the join table explicitly:
<%= f.fields_for :character_sheet_skills do |cs| %>
<fieldset>
<legend><%= cs.name %></legend>
<div class="field">
<%= cs.label :value %>
<%= cs.number_field :value %>
</div>
<%= cs.hidden_field :skill_id %>
</fieldset>
<% end %>
Instead of a hidden fields you could use a select if you want let the user select the skills.
Of course nothing will show up unless you "seed" the inputs:
class CharacterSheetController < ApplicationController
def new
#character_sheet = CharacterSheet.new do |cs|
# this seeds the association so that the fields appear
Skill.all.each do |skill|
cs.character_sheet_skills.new(skill: skill)
end
end
end
def create
#character_sheet = CharacterSheet.new(character_sheet_params)
if #character_sheet.save
redirect_to #character_sheet
else
render :new
end
end
private
def character_sheet_params
params.require(:character_sheet)
.permit(
:foo, :bar, :baz,
character_sheet_skill_attributes: [:skill_id, :value]
)
end
end

mxn relation in Data Models in Ruby on Rails

i have the following Problem:
I have three different data models : customer , periods and request. A request consist of the customer_id and the periods_id (i dont know why its periodS_id because i genereated a scaffold called period). Now if i want to create a request i want to choose a customer and a period so my code in the form.html.erb of requests is:
<div class="field">
<%= f.label 'Kunde auswählen' %><br />
<%= f.collection_select :customer_id, Customer.all, :id, :name %>
</div>
<div class="field">
<%= f.label 'Zeitfenster auswählen' %><br />
<%= f.collection_select :periods_id, Period.all , :id, :description %>
</div>
and this works pretty well. I can choose the name of the customers and peridos but then if i want to create a request i get the error message undefinded method, because he cant display the name of periods in the show.html.erb:
<%= #request.customer.name%>
<%= #request.periods.name%>
My idea is a mistake in the datamodels. I´m not sure if this mxn relationship works. Here the datamodels:
class Request < ActiveRecord::Base
validates :periods_id, :customer_id, presence: true
belongs_to :customer
belongs_to :period
end
class Period < ActiveRecord::Base
has_many :requests, :dependent => :destroy
has_many :customers, :through => :requests
end
class Customer < ActiveRecord::Base
has_many :requests, :dependent => :destroy
has_many :periods, :through => :requests
end
and it works for customer: if i delete period.name he can show the name of the specific customer.
Any ideas ?
Its give error of period because request model belongs to period, not request has_many period
<%= #request.period.name%>
Change .periods to .period

Nested Attributes for a Rich Join Table, using simple_form Rails

I want to create a form that has nested attributes, which populates a record within a rich join table. (That created record within the rich join table of course should have the appropriate foreign keys.)
I have yet to find a thorough answer on creating nested form fields on a has_many :through relationship. Please help!
For this example, I have a user form. Within that form, I am also trying to populate a record within the users_pets table (rich join table).
Additional question: are rich join models supposed to be singular or plural? Example: app/models/owners_pets.rb or app/models/owners_pet.rb.
app/models/owner.rb
class Owner < ActiveRecord::Base
has_many :owners_pets, allow_destroy: true
has_many :pets, through: :owners_pets
end
app/models/pet.rb
class Pet < ActiveRecord::Base
has_many :owners_pets, allow_destroy: true
has_many :owners, through: :owners_pets
end
app/models/owners_pets.rb
class OwnersPet < ActiveRecord::Base
belongs_to :owners
belongs_to :pets
end
app/controller/owners.rb
def owner_params
params.require(:owner).permit(:first_name, owners_pets_attributes: [:id, :pet_name, :pet_id])
end
app/views/owners/_form.html.erb
<%= simple_form_for(#owner) do |f| %>
<%= f.input :first_name %>
<%= f.simple_fields_for :owners_pets do |ff|
<%= ff.input :pet_name %>
<% end %>
<div>
<%= f.button :submit %>
</div>
<% end %>
Here is the answer, thanks to a bunch of help from a mentor. It helps me to keep in mind that rich join naming conventions should NOT be pluralized at the very end, just like other non-rich-join models. Ex: book_page.rb NOT books_pages.rb. Even books_page.rb would work (just update your strong params and database table accordingly). The important part is that the entire model must follow the rails convention of the model being singular (no 's' on the end).
Below in the rich join model, I made the decision to name it the completely singular version: owner_pet.rb as opposed to the other version: owners_pet.rb. (Therefore of course, my database table is named: owner_pets)
app/models/owner.rb
class Owner < ActiveRecord::Base
has_many :owner_pets
has_many :pets, through: :owner_pets
accepts_nested_attributes_for :owner_pets, allow_destroy: true
end
app/models/pet.rb
class Pet < ActiveRecord::Base
has_many :owner_pets
has_many :owners, through: :owner_pets
end
app/models/owner_pet.rb
class OwnerPet < ActiveRecord::Base
belongs_to :owner
belongs_to :pet
end
app/controller/owners.rb
def new
#owner = Owner.new
#owner.owner_pets.build
end
private
def owner_params
params.require(:owner).permit(:first_name, owner_pets_attributes: [:_destroy, :id, :pet_name, :pet_id, :owner_id])
end
app/views/owners/_form.html.erb
<%= simple_form_for(#owner) do |f| %>
<%= f.input :first_name %>
<%= f.simple_fields_for :owner_pets do |ff| %>
<%= ff.input :pet_name %>
<%= ff.input :pet_id, collection: Pet.all, label_method: "pet_type" %>
<% end %>
<div>
<%= f.button :submit %>
</div>
<% end %>
Your join table is the problem:
It should be belongs_to :owners belongs_to :pets for the join table to work
Plus the rich join model should be pluralised, as in: owners_pets

Rails 3 has_many through association unable to show or edit nested associations

I am new to Ruby and am learning OTJ. I have been grinding on this problem for days and have read lots of other posts regarding the has_many :through pattern.
I am using RubyMine 5.4 and ruby-1.9.3-p194 with MySQL
I have three models and I need to be able to add, remove and edit the nested relationships:
The idea is that any user can have many groups of business competitors. A group (with the same group_id) has one target competitor and it's competition. Each competitor is a BizEntity with it's associated attributes. Each competitor can be a member of many different competitive groups.
I am trying to get the many to many working so I can select a user and then view/edit the users groups and related competitors.
I have run RubyMine's Inspect Code... on the models and it is clean. Yet, the Rails Model Dependency Diagram indicates (red line with ?'s at both ends) that there is an issue with the Users to BizEntity relationship.
Any assistance would be greatly appreciated.
Thanks!
Models are as follows:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email, :password_digest
validates_uniqueness_of :email
has_many :competitive_insightses, :class_name => 'CompetitiveInsights'
has_many :biz_entitieses, :through => :competitive_insightses, :class_name => 'CompetitiveInsights', :source => :biz_entities
end
class CompetitiveInsights < ActiveRecord::Base
attr_accessible :biz_entity_id, :user_id, :group_id, :position, :target
belongs_to :user
belongs_to :biz_entities
end
class BizEntities < ActiveRecord::Base
attr_accessible :name, :ticker
accepts_nested_attributes_for :users
has_many :competitive_insightses, :class_name => 'CompetitiveInsights'
has_many :users, :through => :competitive_insightses, :class_name => 'CompetitiveInsights'
end
In my Controller the two commented lines cause the same type of errors
"undefined method `biz_entitieses' for nil:NilClass"
class CompetitiveInsightsController < ApplicationController
def index
#my_companies = CompetitiveInsights.find_all_by_user_id(current_user)
#competitive_insights = current_user.competitive_insightses
#biz_entitieses = current_user.biz_entitieses
if (#my_companies.nil?)
#my_companies = CompetitiveInsights.new(current_user)
#my_companies.save!
end
end
end
The edit.html.erb is not yet able to edit the biz_entity fields and throws the following error:
"undefined method `competitive_insight_competitive_insight_path' for #<#:0x007fc1fae4efd0>"
<%= form_for #my_companies do |f| %>
<div class="field">
<%= f.label :group_id %><br />
<%= f.text_field :group_id %>
</div>
<%= f.fields_for :biz_entities do |biz_entity| %>
<div class="field">
<%= biz_entity.label :ticker %><br />
<%= biz_entity.text_field :ticker %>
</div>
<% end %>
<div class="actions"><%= f.submit %></div>
<% end %>

Correct way to nest form for join model?

I'm trying to nest a form for my Producttracklisting has_many through join model in my product show view. What is the correct way to do this? I'm getting various errors for my various failed attempts.
The models are as follows:
class Product < ActiveRecord::Base
has_many :producttracklistings
has_many :tracks, :through => :producttracklistings
end
class Track < ActiveRecord::Base
has_many :producttracklistings
has_many :products, :through => :producttracklistings
end
class Producttracklisting < ActiveRecord::Base
belongs_to :product
belongs_to :track
end
The form is as follows:
<%= form_for(#producttracklisting) do |f| %>
<%= f.label :track_id %>
<%= f.text_field :track_id %>
<%= f.label :product_id %>
<%= f.text_field :product_id %>
<%= f.submit %>
<% end %>
And i'm trying to bring this into product/show using:
<%= render 'producttracklistings/form' %>
With all of the above I get a "undefined method `model_name' for NilClass:Class"
Thanks in advance.
What you're looking for is accepts_nested_attributes in combination with fields_for.
See this RailsCast Part 1 and part 2 for a detailed tutorial.
PS:
I'd suggest to name your model ProductTrackListing, which results in a table named product_track_listings. This is far more readable and "the rails way"

Resources