I am having trouble rendering a model's attributes in foreign model's view. I think it is a nested attributes problem because f.bike works but f.bike.biketype gives an undefined method error. Below is my error and code. Any help would be great. Thanks!
Error from Browser:
NoMethodError in Carts#show
Showing /Users/willdennis/rails_projects/spinlister/app/views/carts/show.html.erb where line #4 raised:
undefined method `biketype' for nil:NilClass
Extracted source (around line #4):
1: <h2>Your Cart</h2>
2: <ul>
3: <% #cart.line_items.each do |f| %>
4: <li><%= f.bike.biketype %></li>
5: <% end %>
6: </ul>
views/carts/show.html.erb
<h2>Your Cart</h2>
<ul>
<% #cart.line_items.each do |f| %>
<li><%= f.bike.biketype %></li>
<% end %>
</ul>
cart.rb
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :line_items
attr_accessible :bike_id, :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :bike
belongs_to :cart
accepts_nested_attributes_for :bike, :cart
attr_accessible :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
bike.rb
class Bike < ActiveRecord::Base
has_many :line_items
attr_accessible :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
carts_controller.rb
def show
#cart = Cart.find(params[:id])
end
Your error is telling you that the object f.bike is of class nil. You would have been expecting it to be an instance of class Bike. Obviously you have a line item in your view that doesn't have a bike record attached to it.
To take care of the error so your view will display just add a check for blank?
li><%= f.bike.biketype unless f.bike.blank? || f.bike.biketype.blank? %></li>
As to why you have a line item with no bike? Well that's an entirely different question and there is not enough info here to answer that.
UPDATE Based on comments below
Your button_to looks a little wrong. Try
<%= button_to "Rent this Bicycle!", {line_items_path(:bike_id => #bike)}, {:id => "rentthisbike"} %>
Using the curly braces ensures that Rails knows the second param is a css style not a param to be passed into the controllers action
To check if your bike id is getting into the controller then check the params that are actually getting into the controller action that add the bike to the line item. You will be able to find this in your development.log file, find the post request for the action and one of the first lines in that action will list all the params. If you see the bike_id in that list then either one of 2 possible situations is occurring.
1) You are not accessing the params hash properly to get the bike id from it
or
2) You are not setting the bike ID on the line item before saving the record.
You should be able to access the id using params[:bike_id]
If you don't see the bike id then you need to look again at the button_to code to see why it's not passing the bike ID
End of update
Hope that helps
Related
I got a variable in a nested model (account) belonging the user model in which there is a column for :skills. in my edit_user_path, i would like to be able to save multiple skills into that column via checkboxes. For doing that I permitted the skills in the controller as array, even though it is saved as string into the database.
My controller:
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :account,
account_attributes:[:id, :username, {:skills => []}, :description, :location, :avatar, :tags, :tag_list])
end
If i save multiple values via checkboxes into this variable inside of a nested form, i do it like this:
<% skills = ["Coding", "Design", "Petting Cats"] %>
<% skills.each do |skill| %>
<div class="form-check form-check-inline">
<div class="custom-control custom-checkbox">
<%= form.check_box :skills, { multiple: true, class:"custom-control-input",id: skill }, skill, false %>
<%= form.label skill, class:"custom-control-label", for: skill %>
</div>
</div>
<% end %>
This works and the values are getting saved as an array, but oddly the array once saved into the database looks like this:
"[\"Artist\", \"Mixing\", \"Mastering\"]"
instead of this:
["Artist", "Mixing", "Mastering"]
Which leads to troubles, since i would like to iterate through all users later "filtering" for certain skills, like User.account.where(skills: "Petting Cats") if a user has Petting Cats saved somewhere inside of the array.
For Development i am using SQLite, for production PostgresQL.
How do i save multiple strings into a string variable as clean array without mess, and how to iterate through the array later with a where query method?
You're falling into the common double whammy noob trap of using an array column and serialize.
The reason you are getting "[\"Artist\", \"Mixing\", \"Mastering\"]" stored in is that you are using serialize with a native array column. serialize is an old hack to store stuff in varchar/text columns and the driver actually natively converts it to an array. When you use serialize with a native array/json/hstore column your actually casting the value into a string before the driver casts it. The result is:
["[\"Artist\", \"Mixing\", \"Mastering\"]"]
Removing serialize will fix the immediate problem but you're still left with a substandard solution compared to actually doing the job right:
# rails g model skill name:string:uniq
class Skill < ApplicationRecord
validates :name, uniqueness: true, presence: true
has_many :user_skills, dependent: :destroy
has_many :users, through: :user_skills
end
# rails g model user_skill user:belongs_to skill:belongs_to
# use `add_index :user_skills, [:user_id, :skill_id], unique: true` to ensure uniqueness
class UserSkill < ApplicationRecord
validates_uniqueness_of :user_id, scope: :skill_id
belongs_to :user
belongs_to :skill
end
class User < ApplicationRecord
has_many :user_skills, dependent: :destroy
has_many :skills, through: :user_skills
end
<%= form_with(model: #user) do |form| %>
<div class="field">
<%= form.label :skill_ids %>
<%= form.collection_select :skill_ids, Skill.all, :id, :name %>
</div>
<% end %>
def user_params
params.require(:user).permit(
:email, :password, :password_confirmation, :account,
account_attributes: [
:id, :username, :description, :location, :avatar, :tags, :tag_list,
],
skill_ids: []
)
end
This gives you:
Queryable data
Referential integrity
Normalization
Encapsulation
ActiveRecord Assocations!
The pleasure of not looking like an idiot in a review/audit
This is a new error to me, and struggling to resolve it. It also states: Roaster(#70130698993440) expected, got "1" which is an instance of String(#70130675908140)
It's highlighting my create method in my Roasts Controller:
def create
#roast = Roast.new(roast_params)
The scenario is that I'm trying to create a triple nested form. for three models Roasts Countries and Regions where roasts has many countries and countries has many regions.
I'm assuming there is something wrong with the roast params, but I can see what it is. I have added the associations there for the nested models
def roast_params
params.require(:roast).permit(:roaster, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, :countries_attributes => [:country_name, :regions_attributes => [:region_name]])
end
my form
<div class="form-group">
<%= form.fields_for :countries do |countries_form| %>
<%= countries_form.label :country %>
<%= countries_form.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= form.fields_for :regions do |regions_form| %>
<%= regions_form.label :region %>
<%= regions_form.text_field :region_name, class: "form-control" %>
<% end %>
<% end %>
</div>
Roast Controller
...
def new
#roast = Roast.new
#roast.countries.build.regions.build
end
...
roast model
class Roast < ApplicationRecord
has_many :tastings
has_many :countries
has_many :notes, through: :tastings
has_many :comments, as: :commentable
belongs_to :roaster
accepts_nested_attributes_for :countries
country model
class Country < ApplicationRecord
has_many :regions, inverse_of: :country
accepts_nested_attributes_for :regions
belongs_to :roasts
region model
class Region < ApplicationRecord
belongs_to :country
I've nested the regions params in the country params, is that correct? I also saw on SO other issues with suggestions for setting config.cache_classes to true in development.rb but that didn't help here.
Update
So looking at this further, I believe it's not related to the nested forms, but rather a collection_select I'm using.
<%= form.label :roaster, class: 'control-label' %>
<%= form.collection_select(:roaster, Roaster.order(:roaster_name).all, :id, :roaster_name, prompt: true, class: "form-control") %>
So this select is pulling the roaster_name from a model called Roaster.
My params now look like the below:
params.require(:roast).permit(:roaster_name, :roaster, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, :countries_attributes => [:country_id, :country_name, :regions_attributes => [:region_id, :region_name]])
And looking at the console when submitting the form, it seems that just the :id of Roaster is getting passed, rather than the value of :roaster_name.
{"utf8"=>"✓",
"authenticity_token"=>"EG+zty85IiVsgipm1pjSAEZ7M66ELWefLq8Znux+cf89sSnVXxielRr1IaSS9+cJvdQD8g1D4+v2KqtKEwh6gw==",
"roast"=>{"roaster"=>"1", "name"=>"Espress", "countries_attributes"=>{"0"=>{"country_name"=>"UK"}}, "regions"=>{"region_name"=>"Highlands"}, "bestfor"=>"", "roast"=>"", "tastingnotes"=>""},
"commit"=>"Create Roast"}
Can't work this out
ActiveRecord::AssociationTypeMismatch is raised when an association-setter (Roast#roaster= in this case) is called with a value that is not an instance of the expected class. Roaster was expected, got String.
The issue seems to be with passing roaster in as a param, which is "1" (String) in your example. I'm guessing this is actually an ID of a Roaster, the form code in the question does not show it.
Perhaps you meant to permit and pass a roaster_id param?
def roast_params
params.require(:roast).permit(:roaster_id, # ...
end
I have a model location which has many messages. I am trying to let users who are browsing locations to send messages to location owners.
I have location.rb
has_many :messages
accepts_nested_attributes_for :messages
and message.rb
belongs_to :location
In locations_controller.rb
def location_params
params.require(:location).permit(:name,
:user_id,
:image,
:latitude,
:longitude,
location_images_attributes: [:id, :location_id, :location_image, :_destroy],
messages_attributes: [:id, :location_id, :from_email, :to_email, :content])
end
Currently i have in my view the following code:
<%= simple_form_for #location do |l| %>
<%= l.simple_fields_for :messages, #location.messages.build do |m| %>
<%= m.input :content %>
<%= m.input :to_email, :input_html => {:value => #location.user.email}, as: :hidden %>
<% end %>
<%= l.button :submit %>
<% end %>
I don't want to set the value of the email field via hidden field, but i want to pass value from controller. Or a model. Please advise.
Maybe try this approach in your Location model:
class Location < ActiveRecord::Base
has_many :messages, after_add: :set_email
accepts_nested_attributes_for :messages
def set_email(message)
message.to_email = user.email
end
end
Basically you have to register a method for Location, when new message is added to messages set.
In this example I named it set_email, which takes the message object as it's argument, and you can freely modify it. I'm just setting the to_email based on location's email.
Hope this solves your problem!
You could do something like this in the Message model:
before_create :set_to_email
def set_to_email
self.to_email = self.location.user.email
end
Downside is that on every create action a few additional database lookups are being performed. So on the grand scale this is not an ideal solution in terms of performance optimization.
I'm developing an app for college where a user can log on & upload details of a hiking trail.
So far everything is working & I have also implemented a nested form for photos in each hiking trail. A user can log-on & create a hike.
I would like to display all the hikes which the user created in there show/profile page but when I've set up the relationships in my database & the has_many & belongs_to options in my model. I've also tried to do this with nested accepts_nested_attributes_for :hikingtrails it does none of this works.
I've checked my database when a hikingtrail is created by a user it is not updating the user_id field in the table.
I'm not sure if I'm approaching this entirely the wrong way, should I be looking at polymorphic associations?
class User < ActiveRecord::Base
attr_accessible :user_name, :email, :password, :password_confirmation, :photos_attributes, :hikingtrails_attributes
has_many :hikingtrails
accepts_nested_attributes_for :hikingtrails, :allow_destroy => :true, :reject_if => :all_blank
class Hikingtrail < ActiveRecord::Base
attr_accessible :description, :name, :looped, :photos_attributes,:directions_attributes, :user_id
has_many :photos
has_many :trails
has_many :directions
belongs_to :user
users/show.html.erb
<div class="page-header">
<h1>Your Profile</h1>
</div>
<p>
<b>username:</b>
<%= #user.user_name %>
</p>
<p>
<b>email:</b>
<%= #user.email %>
</p>
<h4>Small Photos</h4>
<% #user.photos.each do |photo| %>
<%= image_tag photo.image_url(:thumb).to_s %>
<% end %>
<h4>Hiking Trails</h4>
<% #user.hikingtrails.each do |hk| %>
<%= hk.name %>
<% end %>
<%= link_to "Edit your Profile", edit_user_path(current_user), :class => 'btn btn-mini' %>
You didn't add :user_id to your accessible attributes in the Hikingtrail model. Try the following:
attr_accessible :description,
:duration_hours,
:duration_mins,
:name,
:looped,
:addr_1,
:addr_2,
:addr_3,
:country,
:latitude,
:longitude,
:photos_attributes,
:trails_attributes,
:directions_attributes,
:user_id
UPDATE:
After seeing the form code, I think it's probably not necessary to do the above and could potentially also be unsafe. Instead, don't set the user_id through mass assignment, but handle user assignment in your controller like so:
class HikingtrailsController < ApplicationController
# ...
def create
#hikingtrail = Hikingtrail.new(params[:hikingtrail])
#hikingtrail.user = current_user
if #hikingtrail.save
# ...
else
# ...
end
end
end
Hope this helps :)
following up with this topic:
(Rails) How to display child records (one-to-many) in their parent's form?
I am creating a category that may have many products. So, I use the Formtastic as user Chandra Patni suggested. But I find there is a problem when I added attr_accessible to the product.rb.
class Product < ActiveRecord::Base
validates_presence_of :title, :price
validates_numericality_of :price
validates_uniqueness_of :title
attr_accessible :category_id, :name
belongs_to :category
end
and this is the category.rb:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :products
end
After I added the attr_accessible :category_id, :name , the validation gets crazy, no matter I type, it treats me as null in the text value. But after I remove the attr_accessible :category_id, :name all stuff works.
One more thing, in the products/new.html.erb , I created this to input the product information,
<% semantic_form_for #product do |f| %>
<% f.inputs do %>
<%= f.input :title %>
<%= f.input :price %>
<%= f.input :photoPath %>
<%= f.input :category %>
<% end %>
<%= f.buttons %>
<% end %>
But I find that it return the :category id instead of the category name. What should I do?
Thx in advanced.
You will need to add other attributes to attr_accessible for rails to perform mass assignment.
attr_accessible :category_id, :name, :title, :price, :photoPath
Rails will only do mass assignment for attributes specified in attr_accessible (white list) if you have one. It is necessary from security point of view. Rails also provides a way to blacklist mass assignments via attr_protected method.
You should see a drop down for category if you have name attribute in your Category model. See this and this.
class Category < ActiveRecord::Base
has_many :products
attr_accessible :name
end