I seem to be having a problem with receiving products through my join table, it's giving me a strange error as it seems to be receiving no ID for my order. I can only assume that this is because the order has not been created yet, but I am creating the order during this step anyway, so the order doesn't have an ID yet. So this is my problem.
Here is the error I recieve:
ActiveRecord::RecordNotFound in OrdersController#create
Couldn't find Product with ID=1 for Order with ID=
Rails.root: /BillingSystem
Application Trace | Framework Trace | Full Trace
app/controllers/orders_controller.rb:10:in `new'
app/controllers/orders_controller.rb:10:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"jE2wdERoxE7PKwBhN60KAfguxwAq8qdW4wbru51SMFg=",
"order"=>{"client_id"=>"1",
"products_attributes"=>{"1368396234677"=>{"id"=>"1",
"_destroy"=>"false"}}},
"commit"=>"Create Order"}
Show session dump
Show env dump
Response
Headers:
None
New Order View:
<% if current_user %>
<div id="dashboard">
<div id="logo"></div>
<table id="go_back_link_container">
<tr>
<td>
<div class="go_back_link">
<%= link_to "<- Go Back", "/orders/view" %>
</div>
</td>
<td>
<div id="user_display">
Logged in as <%= current_user.email %>.
<%= link_to "Log out", log_out_path %>
</div>
</td>
</tr>
</table>
<%= form_for #order, method: :post do |f| %>
<% if #order.errors.any? %>
<div class="error_messages">
<% for message in #order.errors.full_messages %>
* <%= message %> <br>
<% end %>
</div>
<% end %>
<p>
<%= f.label 'Select The Client' %><br />
<%= select :order, :client_id, Client.all().collect { |c| [ (c.firstname + " " + c.surname), c.id ] } %>
</p>
<%= f.fields_for :products do |pf| %>
<% #render 'product_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Product", f, :products %>
<p class="button"><%= f.submit %></p>
<% end %>
<% flash.each do |name, msg| %>
<%= content_tag :div, "* " + msg, :id => "flash_#{name}" %><br />
<% end %>
<div id="copyright-notice"><div id="copyright_border">Copyright © Conner McCabe, all rights reserved.</div></div>
</div>
<% else %>
<script type="text/javascript">
window.location="<%= root_url %>"
</script>
<% end %>
Order Model:
class Order < ActiveRecord::Base
has_many :orderedproducts
has_many :products, through: :orderedproducts
has_one :client
attr_accessible :client_id, :order_total, :delivery_date, :products, :products_attributes
accepts_nested_attributes_for :products, :allow_destroy => true
before_save :generate_total
def generate_total
self.order_total = self.products.map(&:product_price).sum
end
end
Orders Controller:
class OrdersController < ApplicationController
def view
#orders = Order.all
end
def new
#order = Order.new
end
def create
#order = Order.new(params[:order])
if #order.save
redirect_to '/orders/view', :notice => "Order Created!"
else
render "new"
end
end
end
Product Fields Partial:
<fieldset>
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
Products Model:
class Product < ActiveRecord::Base
#This line makes these elements accessible outside of the class.
attr_accessible :product_name, :product_price, :product_quantity, :product_supplier
has_many :orderedproducts
has_many :orders, through: :orderedproducts
#These attributes ensure that the data entered for each element is valid and present.
validates_presence_of :product_name
validates_presence_of :product_price
validates_numericality_of :product_price
validates_presence_of :product_quantity
validates_numericality_of :product_quantity
validates_presence_of :product_supplier
end
Application Helper:
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
Ordered Products Model:
class Orderedproduct < ActiveRecord::Base
attr_accessible :order_id, :product_id, :quantity_ordered
belongs_to :order
belongs_to :product
end
I have listed every possible file that could contain an error, I know it's a bit excessive, but it's everything that is to do with it and better I include it than not at all.
I also followed this railscast guide: http://railscasts.com/episodes/196-nested-model-form-revised
To get to where I am, I edited it slightly so that it was suitable for my application.
Thanks in advance.
We had a similar issue on a project, except the relation was singular. The problem is that ActiveRecord is looking for an existing association; something like order.products.find(1). Since order is as new record this doesn't work.
You could create your own products_attributes= method and define the correct behaviour. But I think that you could just use nested attributes for the join model (Orderedproduct) instead of Product.
class Order
accepts_nested_attributes_for :orderedproducts
end
Then adjust the form fields appropriately. In the new form
f.fields_for :products do |pf| becomes f.fields_for :orderedproducts do |pf|
In the fields partial
<%= f.select :id, Product.all().collect {|p| [ p.product_name, p.id ] } %> becomes <%= f.select :product_id, Product.all().collect {|p| [ p.product_name, p.id ] } %>
Related
I am trying to display the new form page from another controller's page, I am used nested resources.
I am getting this error
undefined method `sublet_posts' for nil:NilClass
Student Model
class Student < ApplicationRecord
has_many :sublet_posts, :dependent => :destroy
end
Sublet_post model
class SubletPost < ApplicationRecord
belongs_to :student
end
route.rb
resources :students do
resources :sublet_posts
end
Student show view
<p id="notice"><%= notice %></p>
...
...
...
<h3>Sublet Post</h3>
<% #student.sublet_posts.each do |sublet_post| %>
<%= sublet_post.description %>
<%= link_to "Edit", edit_student_sublet_post_path(#student, sublet_post) %>
<% end %>
<h3>Add Sublet Post</h3>
<%= link_to "Add", new_student_sublet_post_path(#student)%>
Sublet_Post Controller
class SubletPostsController < ApplicationController
before_action :set_sublet_post, only: [:edit, :update, :destroy]
# POST /sublet_posts
# POST /sublet_posts.json
def create
#student = Student.find(params[:student_id])
#sublet_post = #student.create(sublet_post_params)
#sublet_post.student_id = current_member_id
redirect_to student_path(#student)
end
# PATCH/PUT /sublet_posts/1
# PATCH/PUT /sublet_posts/1.json
def update
#student = Student.find(params[:student_id])
#sublet_post = #student.sublet_posts.update(sublet_post_params)
redirect_to student_path(#student)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_sublet_post
#sublet_post = SubletPost.find(params[:id])
end
# Only allow a list of trusted parameters through.
def sublet_post_params
params.require(:sublet_post).permit(......)
end
end
Sublet Post form view
<%= form_for [#student, #student.sublet_posts.build] do |form| %>. <----------------- Error
<% if sublet_post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(sublet_post.errors.count, "error") %> prohibited this sublet_post from being saved:</h2>
<ul>
<% sublet_post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
Any help will be very appreciated. I didn't think the student controller was needed so I left it out.
I'm trying to upload pictures to my rails app using the carrierwave gem, but can't figure out how to get the photos to display with the image_tag helper. I'm actually not sure if the photos are uploading because I get a NoMethodError for .image_url. The photos model belongs to the places model.
<%= simple_form_for #photo, url: place_photos_path(#place) do |f| %>
<%= f.input :caption %>
<%= f.input :picture %>
<%= f.submit "Add a photo", class: 'btn btn-primary' %>
<% end %>
<% if #place.photos.any? %>
<% #place.photos.each do |photo| %>
<%= photo.caption%>
<%= image_tag photo.image_url.to_s if photo.image_url.present? %>
<% end %>
<% end %>
class Photo < ApplicationRecord
belongs_to :user
belongs_to :place
mount_uploader :picture, PictureUploader
end
class PictureUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
class Place < ApplicationRecord
belongs_to :user
has_many :comments
has_many :photos
geocoded_by :address
after_validation :geocode
validates :name, presence: true
validates :address, presence: true
validates :description, presence: true
end
end
If you post your model and uploader code it will make bit easier to trace down the problem:
I believe you are using this code as specified in carrierwave documentation:
class Photo < ActiveRecord::Base
mount_uploader :picture, ImageUploader
end
Then all you have to do is use this:
<%= image_tag photo.picture.url, class: 'class-name' %>
For more information about uploader read from here https://github.com/carrierwaveuploader/carrierwave#getting-started
In your picture_uploader.rb
class PictureUploader < CarrierWave::Uploader::Base
#uncomment this line
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
I have a Rails 5.1 app that consumes the Google Books API and I need to pre-populate nested fields in a form. There are two ways to create a Book.
Firstly, through the normal /books/new form, which accepts_nested_attributes_for :authors with a has_many: through association. Here I am using cocoon gem and everything is working great.
Secondly, a user can create a Book by searching the Google Books API using an ISBN number. This data then pre-populates a form before being submitted to the create action in the BooksController. I have managed to get this working great apart from the ability to correctly submit the nested Author data.
I currently have each author populate a field in the form, yet when I come to submit the data only the last item in the author array (in the case of a book with multiple authors) gets saved.
I believe this is because the form html has the same name and id for both fields as below. How do I get this form to submit both authors?
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
books_controller.rb
class BooksController < ApplicationController
before_action :authenticate_user!
before_action :set_book, except: [:index, :new, :create, :new_book, :submit_book]
def create
#book = current_user.books.create(book_params)
#book.authors.each {|author| author.user_id = current_user.id}
if #book.save
redirect_to book_path(#book)
else
render :new
end
end
def new_book
end
def submit_book
#book = Book.new
#book.authors.new
#response = GoogleBooks.new(params[:q], #book)
end
private
def set_book
#book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :subtitle, :description, author_ids:[], authors_attributes: [:id, :name, :_destroy])
end
end
book.rb
class Book < ApplicationRecord
has_many :book_authors
has_many :authors, through: :book_authors
accepts_nested_attributes_for :authors, allow_destroy: true
validates :title, presence: true
validates_associated :authors
end
google_books.rb
class GoogleBooks
include HTTParty
base_uri 'https://www.googleapis.com/books/v1'
def initialize(isbn, book)
#query = self.class.get("/volumes?q=isbn:#{isbn}")
#book = book
end
def title
#query['items'].first['volumeInfo']['title']
end
def subtitle
#query['items'].first['volumeInfo']['subtitle']
end
def description
#query['items'].first['volumeInfo']['description']
end
def authors
#query['items'].first['volumeInfo']['authors']
#=> ['John J. Ratey', 'Richard Manning']
end
end
submit_book.html.erb
<%= form_for #book do |f| %>
<%= f.text_field :title, value: #response.title %>
<%= f.text_field :subtitle, value: #response.subtitle %>
<%= f.text_field :description, value: #response.description %>
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<% #response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
<%= f.submit %>
Worked it out.
Was a case of changing
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<% #response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
to this
<% #response.authors.each do |author| %>
<%= f.fields_for :authors, #book.authors.build do |authors_fields| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
Which produces the following correct HTML:
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][1][name]" id="book_authors_attributes_1_name">
So i asked this question a while back before but i still cant seem to get this right . im trying to make my comment model make comments for both topics and posts. i just want Comments controller to be able to handle comments going to post or topic.
routes :
resources :topics, :posts do
resources :comments, only: [:create, :destroy]
end
Topic Model:
has_many :comments, dependent: :destroy
Comment Model:
belongs_to :post
belongs_to :topic
Post:
has_many :comments, dependent: :destroy
Comments Controller :
def create
if params[:post_id]
#parent = Post.find(params[:post_id])
#comment = #parent.comments.new(comment_params)
#comment.user = current_user
if #comment.save
flash[:notice] = "Comment saved successfully."
redirect_to [#parent.topic, #parent]
else
flash[:alert] = "Comment failed to save."
redirect_to [#parent.topic, #parent]
end
elsif params[:topic_id]
#parent = Topic.find(params[:topic_id])
#comment = #parent.comments.new(comment_params)
#comment.user = current_user
if #comment.save
flash[:notice] = "Comment saved successfully."
else
flash[:alert] = "Comment failed to save."
end
end
end
def comment_params
params.require(:comment).permit(:body)
end
comment/form.html
<%= form_for [#parent, #comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
I keep getting undefined local variable or method parent when trying to go to my topic/show view
also how can i implement comments to show up on topic/post view
undefined local variable or method parent
You need to change your local variables (parent & comment) to instance variables (#parent & #comment) in the controller action and as well in the view in order to use those in the view.
The below should work
<%= form_for [#parent, #comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
Also I've noticed that there are two #parent & #parant variables defined in the controller action. If its a typo then correct it.
So i am getting trouble in saving form data,.Any help will b appreciable
form is submitted without getting any error, but in database, nothing is stored
i am new on rails
users_controller
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:User])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
end
new.html.erb
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
<p> Username:</br> <%= f.text_field :username %> </p>
<p> Email:</br> <%= f.text_field :email %> </p>
<p> Password:</br> <%= f.password_field :password %></p>
<p> Password Confirmation:</br> <%= f.password_field :password_confirmation %> </p>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
in app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :password
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
validates :username, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :email, :presence => true, :uniqueness => true #:format => EMAIL_REGEX
validates :password, :presence =>true #:confirmation => true #password_confirmation attr
validates_length_of :password, :in => 6..20, :on => :create
end
In users_controller, create method, you are using
#user = User.new(params[:User])
replace it with following code, hope it will work fine.
#user = User.new(params[:user])
And,also use strong params if you are using rails 4. Like follwoing.
def create
#user = User.new(user_params)
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
else
flash[:notice]= "failed"
flash[:color]="invalid"
end
render "new"
end
private
def user_params
params.require(:user).permit(:username, :account, :email, :password, :password_confirmation)
end
If, it still not works, then, please display your log.
If you are using Rails 4 you need to use strong parameters to whitelist the parameters you want to assign to your models.
This became non-optional in Rails 4 to prevent mass-assignment vulnerabilities where a malicious user can assign any property to a model after Egor Homakovs much publicised Github attack.
Also note that Ruby is case sensitive. This applies to hash keys as well:
irb(main):003:0> hash = { a: 1 }
=> {:a=>1}
irb(main):004:0> hash[:A]
=> nil
Which is why why you do User.new(params[:User]) you are actually doing User.new(nil)
This is a corrected version of your controller
class UsersController < ApplicationController
def new
#user= User.new
end
def create
#user = User.new(params[:User])
if #user.save
flash[:notice]= "you signed up successfully"
flash[:color]= "valid"
# You should redirect instead of rendering the form again
redirect_to #user # or redirect_to root_path
else
flash[:notice]= "failed"
flash[:color]="invalid"
render "new" # Needs to be inside the "else" statement
# Otherwise you will get a double render error
end
end
def user_params
params.require(:user)
.allow(:username, :email, :password, :password_confirmation)
end
end
Added:
You can also simplify your form_for to
<%= form_for(:user) do |f| %>
Rails will by convention route the form to UserController#create.
Also you should use <label> tags for accessibility, as they help people who use assistive technology such as screen readers to find the correct inputs.
By using the built in label helper rails will set up the for attribute and you can translate the label texts with Rails built in I18n functionality.
<% page_title="Signup" %>
<div class="Sign_Form">
<h1>Sign up</h1>
<%= form_for(:user) do |f| %>
<div class="row">
<%= f.label :username %>:</br>
<%= f.text_field :username %>
</div>
<div class="row">
<%= f.label :email %>:</br>
<%= f.text_field :email %>
</div>
<div class="row">
<%= f.label :password %>:</br>
<%= f.password_field :password %>
</div>
<%= f.submit :Signup %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<%# for loops are almost never used in ruby. %>
<%# .each is the idiomatically correct way %>
<% #user.errors.full_messages.each do |message_error| %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>