Rails 4 - How can I show all nested attributes? - ruby-on-rails

I'm new in Rails, I do a model with nested attributes, works perfectly. But in the time to show all nested attributes, I get an error.
Model
class Slide < ActiveRecord::Base
belongs_to :user
has_many :images, :dependent => :destroy
accepts_nested_attributes_for :images, :reject_if => lambda { |a| a[:img_str].blank? }
end
class Image < ActiveRecord::Base
belongs_to :slide
validates :img_str, presence: true
mount_uploader :img_str, AssetUploader
end
slides_controller
def index
#slide = Slide.all
end
slide/view/index
<% #slide.each do |slide| %>
<%= slide.images.img_str %>
<% end %>
Rails show this:
undefined method `img_str' for #Image::ActiveRecord_Associations_CollectionProxy:0x00000003e98dd0
How I can make the the association with image model?

images is a collection of image objects. What you are doing is like calling the attribute of image object on an array of image objects
<% #slide.each do |slide| %>
<%slide.images.each do |image|%>
<%= image.img_str %>
<%end%>
<% end %>

Related

Cannot access attributes from associated model rails

I have three models, ingredient, recipe_ingredient and recipy
class Ingredient < ApplicationRecord
has_many :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipy, :dependent => :destroy
belongs_to :ingredient
end
class Recipy < ApplicationRecord
has_many :recipy_steps
has_many :recipe_ingredients, :dependent => :delete_all
end
I am trying to access the ing_name attribute in the ingredients table from recipies show page.
<% #recipe_ingredients.each do |ing| %>
<p> <%= ing.amount %> <%= ing.unit %>
<%= ing.ingredient.ing_name %>
</p>
def Show from the recipies controller:
def show
#recipe_ingredients = #recipy.recipe_ingredients
end
But I keep receiving the following error msg:
undefined method `ing_name' for nil:NilClass
My ingredient_params:
def ingredient_params
params.require(:ingredient).permit(:ing_name)
end
It does seem to work like this:
<%= Ingredient.where(id: ing.ingredient_id).pluck(:ing_name) %>
But this does not use the connection between the tables if I understand correctly? Any help? Thanks.
You have ingredient nil thats why you got the error.
Must be your controller has some before_action hook to load recipy
class RecipesController < ApplicationController
before_action :load_recipy, only: :show
def show
#recipe_ingredients = #recipy.recipe_ingredients
end
private
def load_recipy
#recipy = Recipy.find(params[:id])
end
end
You can try this to avoid this nil error(undefined method 'ing_name' for nil:NilClass)
<% #recipe_ingredients.each do |ing| %>
<p> <%= ing.amount %> <%= ing.unit %>
<%= ing.try(:ingredient).try(:ing_name) %>
</p>
From Rails 5 by default you got one required option to make ingredient always not nullable
like below
belongs_to :ingredient, required: true
It will also prevent this error of
class RecipeIngredient < ApplicationRecord
belongs_to :recipy, :dependent => :destroy
belongs_to :ingredient, required: true
end
the problem is because inside your show method #recipy is nil,
here is usually code for show
controller
def show
#recipy = Recipy.find(params[:id]) # --> you missed this line
#recipe_ingredients = #recipy.recipe_ingredients # #recipy will be null without line above
end
view
<% #recipe_ingredients.each do |ing| %>
<p> <%= ing.amount %> <%= ing.unit %> <%= ing.ingredient.ing_name %> </p>
<% end %>
I would like also add some suggestion to your model relationship as follow since Ingredient and Recipy shows many to many relationship
class Ingredient < ApplicationRecord
# -> recipe_ingredients -> recipies
has_many :recipe_ingredients, :dependent => :destroy
has_many :recipies, through: :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipy
belongs_to :ingredient
end
class Recipy < ApplicationRecord
has_many :recipy_steps
# -> recipe_ingredients -> ingredients
has_many :recipe_ingredients, :dependent => :destroy
has_many :ingredients, through: :recipe_ingredients
end

Nested checkboxes in Rails

I'm trying to create an event app where each event has multiple tables and each table has multiple people sitting at a table the event has multiple tickets which map the people to the tables that they are sitting at -> in order to achieve this I have created a checkbox nested in the fields_for :tables (which is in turn in the event form) I presume something is wrong with either the strong parameters or the form itself but I have not been able to find any information that provides a solution to the problem.After checking the checkboxes in the form indicating which people are going to be sitting at this table and submitting the form and returning to the form I find that the checkboxes are no longer checked???
here are the contents of my model files
# models
class Event < ActiveRecord::Base
has_many :tables, dependent: :destroy
has_many :people , through: :tickets
has_many :tickets
accepts_nested_attributes_for :tickets, allow_destroy: true
accepts_nested_attributes_for :tables, allow_destroy: true
end
class Table < ActiveRecord::Base
belongs_to :event
has_many :tickets
has_many :people, through: :tickets
end
class Ticket < ActiveRecord::Base
belongs_to :table
belongs_to :person
end
class Person < ActiveRecord::Base
has_many :tickets
has_many :tables, through: :tickets
end
Here is the form with parts omitted for brevity.
<%= form_for(#event) do |f| %>
...
<%= f.fields_for :tables do |builder| %>
<%= render 'table_field', f: builder %>
<% end %>
<%= link_to_add_fields "Add Table", f, :tables %>
...
<% end %>
And here is the checkbox list I have implemented within the table_field.
<% Person.all.each do |person| %>
<div class="field">
<%= check_box_tag "table[people_ids][]", person.id, f.object.people.include?(person) %> <%= f.label [person.first_name, person.last_name].join(" ") %>
</div>
<% end %>
this is the event_params
def event_params
params.require(:event).permit(:name, :description, :start, :end, :latitude, :longitude, :address, :data, :people_ids => [], tables_attributes: [:id, :number, :size, :people_ids => []]).tap do |whitelisted|
whitelisted[:data] = params[:event][:data]
end
How do I get the checkboxes to be persistently checked in this form?
You can use http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes
<%= f.collection_check_boxes(:people_ids, Person.all, :id, :name) do |person| %>
<%= person.label { person.check_box } %>
<% end %>
It will persist data as well.

Form for has_many :trough association with creating object

Problem
With my rails app I'm trying to accomplish something like image below.
Invoice should has many Items. For example Invoice_1 would have in invoice_items container:
[{item: Egg, quantity: 3, unit_price: 4}, {item: Stick, quantity: 4, unit_price: 2}].
How to made a form to add n Items to Invoice?
Or how to edit this form/controller to create fixed number of InvoiceItems for a start. Later I'll figure how to create Items and create associations dynamically in html.
Source
Models
class Item < ActiveRecord::Base
has_many :invoice_items
has_many :invoices, through: :invoice_items
end
class Invoice < ActiveRecord::Base
has_many :invoice_items, inverse_of: :invoice
has_many :items, through: :invoice_items
accepts_nested_attributes_for :invoice_items
validates :items, :length => { :minimum => 1 }
end
class InvoiceItem < ActiveRecord::Base
belongs_to :invoice
belongs_to :item
validates_presence_of :invoice
validates_presence_of :item
accepts_nested_attributes_for :item
end
Invoice controller
# GET /invoices/new
def new
#invoice = Invoice.new
#invoice.invoice_items.build
end
private:
def invoice_params
#params.fetch(:invoice, {})
params.require(:invoice).permit(
:date,
:seller_id,
:client_id,
invoice_items_attributes: [ item_attributes:
[:name, :quantity, :unit, :unit_price_cents, :unit_price_currency, :price_cents, :price_currency ]
],
invoice_name_attributes: [:number, :month, :year],
)
end
Invoice form
<%= form_for(#invoice) do |invoice_form| %>
<%= invoice_form.fields_for :invoice_items do |invoice_item_form| %>
<%= invoice_item_form.fields_for :item do |item_form| %>
<%= item_form.text_field :name %>
<% end %>
<% end %>
<div class="actions">
<%= invoice_form.submit %>
</div>
<% end %>
Based on what I'm reading, you probably just want to focus on making InvoiceItem records built from an invoice. You can build a fixed number (n) of invoice items by using code such as n.times { invoice.invoice_items.build } in the new method in your controller.
When you want to create dynamic fields there is a gem called Cocoon which allows you to dynamically add associated records to a form.
My advice is just to use fields_for :invoice_items as you have done already in your form and omit the fields_for :items. Instead in the invoice_items form add a select_tag that includes all of the Items you want to add to an invoice.

Rails - How can I display one nested attributes

I have a new problem, I Create a web where I upload many images, using nested attributes and polymorphic table, in my index.html I want to show only one image, but I can't find how. But I'm new in rails.
photography.rb
class Photography < ActiveRecord::Base
validates :title, :description, presence: true
belongs_to :user
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images, :reject_if => lambda { |a| a[:img_str].blank? }, :allow_destroy => true
end
image.rb
class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
mount_uploader :img_str, AssetUploader
end
index.html.erb
<% for photo in #photo %>
<%= link_to photo.title, photography_path(photo) %>
<% photo.images.each do |images| %>
<%= images.img_str %>
<% end %>
<% end %>
With the for method I show all the image, try add .first, but says undefined method first for 5:Fixnum. I think that I have to create a helper method, but I not sure. Can anyone help me?. Thanks
Try:
<% for photo in #photo %>
<%= link_to photo.title, photography_path(photo) %>
<%= photo.images.first.img_str if photo.images.any? %>
<% end %>
Also, for is very rarely used in ruby, instead do:
<% #photos.each do |photo| %>

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

Resources