This question already has answers here:
Rails -- use type column without STI?
(5 answers)
Closed 8 years ago.
I'm getting this error and I don't know what to do. It says
Invalid single-table inheritance type: 1 is not a subclass of Game
It says that this error is occuring at the 9 in games_controller.rb.
i.e.
#game = current_user.created_games.build(game_params)
I don't understand what is happening and what I should write here to specify the cause of this error. I copy&paste game.rb, user.rb, application_controller.rb and games_controller.rb here.
game.rb is
class Game < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
end
user.rb is
class User < ActiveRecord::Base
has_many :created_games, class_name: 'Game', foreign_key: :owner_id
def self.find_or_create_from_auth_hash(auth_hash)
provider = auth_hash[:provider]
uid = auth_hash[:uid]
nickname = auth_hash[:info][:nickname]
image_url = auth_hash[:info][:image]
User.find_or_create_by(provider: provider, uid: uid) do |user|
user.nickname = nickname
user.image_url = image_url
end
end
end
application_controller.rb is
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :logged_in?
private
def current_user
return unless session[:user_id]
#current_user ||= User.find(session[:user_id])
end
def logged_in?
!!session[:user_id]
end
def authenticate
return if logged_in?
redirect_to root_path, alert:'Please log in.'
end
def created_by?(user)
return false unless user
owner_id == user.id
end
end
games_controller.rb
class GamesController < ApplicationController
before_action :authenticate
def new
#game = current_user.created_games.build
end
def create
#game = current_user.created_games.build(game_params)
if #game.save
redirect_to #game, notice: 'You created game.'
else
render :new
end
end
private
def game_params
params.require(:game).permit(
:name, :content, :type
)
end
end
Added
new.html.erb
<% now = Time.zone.now %>
<div class="page-header">
<h1>Create Game</h1>
</div>
<%= form_for(#game, class: 'form-horizontal', role:'form') do |f| %>
<% if #game.errors.any? %>
<div class="alert alert-danger">
<ul>
<% #game.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :type %>
<div>
<%= f.select :type, options_for_select: [['basic', 1]], class: 'form-control' %>
</div>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', row: 10 %>
</div>
<%= f.submit 'Create', class: 'btn btn-default', data: { disable_with: 'Creating...' } %>
<% end %>
When I try to create new game, console says as follows.
Started POST "/games" for 127.0.0.1 at 2015-01-29 01:50:29 +0900
Processing by GamesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2W57cDKT2552Xgnh2MLi17uQpcrqDkmxhQJQa1qNuJNsZb0R10l/AU37y+KO9DysCp56aVTFBE/MqRoimMnjYQ==", "game"=>{"name"=>"this is name", "type"=>"1", "content"=>"this is content"}, "commit"=>"Create"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
Completed 500 Internal Server Error in 32ms
ActiveRecord::SubclassNotFound - Invalid single-table inheritance type: 1 is not a subclass of Game:
So to answer the question, you cannot have a column name called type in your migrations. I believe there are ways around this, but it's good practice to just rename your column something else.
Related
I'm completing this airbnb clone course (https://code4startup.com/projects/build-airbnb-with-ruby-on-rails-level-1) but have diverted a bit in order to complete my own project; a marketplace for education camps. Therefore I've added an additional model. It now has User>Listing>Course.
Since adding this course I keep receiving the following error upon running the server and going to localhost:3000/courses/new I've tried searching for the problem on stackoverflow but I'm not experienced enough to describe and therefore find the issue, I'd appreciate any ideas.
Error Message
undefined method `curriculum_type' for #<Listing:0x007fb776ac0f50>
Extracted source (around line #15):
<div class="form-group">
<label>Course Type</label>
<%= f.select :curriculum_type, [["English Language", "English Language"], ["Culture", "Culture"], ["Sports", "Sports"], ["Tech/Science", "Tech/Science"], ["Adventure", "Adventure"], ["Mixture", "Mixture"]],
id: "type", prompt: "Select...", class: "form-control" %>
</div>
</div>
Models
class User < ApplicationRecord
has_many :listings
has_many :courses, :through => :listings
end
class Listing < ApplicationRecord
belongs_to :user
has_many :courses
end
class Course < ApplicationRecord
belongs_to :listing
end
Courses Controller
class CoursesController < ApplicationController
before_action :set_course, except: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def index
#courses = current_user.listings.courses
end
def new
#course = current_user.listings.build
end
def create
#course = listings.build(course_params)
if #course.save
redirect_to listing_course_path(#course), notice: "Saved..."
else
render :new, notice: "Something went wrong..."
end
end
def show
def listing
end
def pricing
end
def description
end
def photo_upload
end
def amenities
end
def location
end
def update
if #course.update(course_params)
flash[:notice] = "Saved..."
else
flash[:notice] = "Something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
private
def set_course
#course = Course.find(params[:id])
end
def room_params
params.require(:course).permit(:name, :course_type, :summary, :address, :course_places, :start_date, :finish_date, :price)
end
end
end
Courses new.html.erb
<div class="panel panel-default">
<div class="panel-heading">
Create your course listing
</div>
<div class="panel-body">
<div class="devise-container">
<%= form_for #course do |f| %>
<div class="row">
</div>
<div class="col_md_4 select">
<div class="form-group">
<label>Course Type</label>
<%= f.select :curriculum_type, [["English Language", "English Language"], ["Culture", "Culture"], ["Sports", "Sports"], ["Tech/Science", "Tech/Science"], ["Adventure", "Adventure"], ["Mixture", "Mixture"]],
id: "type", prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col_md_4 select">
<div class="form-group">
<label>Places</label>
<%= f.select :course_places, [["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5], ["6", 6], ["7", 7]],
id: "places", prompt: "Select...", class: "form-control" %> -->
</div>
</div>
</div>
<div><%= f.submit "Create My Course", class: "btn btn-primary-green" %></div>
<% end %>
</div>
</div>
</div>
In Rails Console I can create all models, but it seems that it is not recognising the Courses Model when I start the server
Your controller is building a Listing object, but your curriculum_type attribute is an attribute for a Course. You'll want to modify your controller to be building a Course object if that's the case, or add the attribute to Listing.
I am having a tough time figuring out how to make a form_object that creates multiple associated objects for a has_many association with the virtus gem.
Below is a contrived example where a form object might be overkill, but it does show the issue I am having:
Lets say there is a user_form object that creates a user record, and then a couple associated user_email records. Here are the models:
# models/user.rb
class User < ApplicationRecord
has_many :user_emails
end
# models/user_email.rb
class UserEmail < ApplicationRecord
belongs_to :user
end
I proceed to create a a form object to represent the user form:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
include Virtus.model
attribute :name, String
attribute :emails, Array[EmailForm]
validates :name, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
puts "I would proceed to create all the necessary objects by hand"
# user = User.create(name: name)
# emails.each do |email_form|
# UserEmail.create(user: user, email: email_form.email_text)
# end
end
end
One will notice in the UserForm class that I have the attribute :emails, Array[EmailForm]. This is an attempt to validate and capture the data that will be persisted for the associated user_email records. Here is the Embedded Value form for a user_email record:
# app/forms/email_form.rb
# Note: this form is an "Embedded Value" Form Utilized in user_form.rb
class EmailForm
include ActiveModel::Model
include Virtus.model
attribute :email_text, String
validates :email_text, presence: true
end
Now I will go ahead and show the users_controller which sets up the user_form.
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user_form = UserForm.new
#user_form.emails = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
#user_form = UserForm.new(user_form_params)
if #user_form.save
redirect_to #user, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {emails: [:email_text]})
end
end
The new.html.erb:
<h1>New User</h1>
<%= render 'form', user_form: #user_form %>
And the _form.html.erb:
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this User from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<% unique_index = 0 %>
<% f.object.emails.each do |email| %>
<%= label_tag "user_form[emails][#{unique_index}][email_text]","Email" %>
<%= text_field_tag "user_form[emails][#{unique_index}][email_text]" %>
<% unique_index += 1 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Note: If there is an easier, more conventional way to display the inputs for the user_emails in this form object: let me know. I could not get fields_for to work. As shown above: I had to write out the name attributes by hand.
The good news is that the form does render:
The html of the form looks ok to me:
When the above input is submitted: Here is the params hash:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "emails"=>{"0"=>{"email_text"=>"foofoo"}, "1"=>{"email_text"=>"bazzbazz"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}
The params hash looks ok to me.
In the logs I get two deprecation warnings which makes me think that virtus might be outdated and thus no longer a working solution for form objects in rails:
DEPRECATION WARNING: Method to_hash is deprecated and will be removed in Rails 5.1, as ActionController::Parameters no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html (called from new at (pry):1)
DEPRECATION WARNING: Method to_a is deprecated and will be removed in Rails 5.1, as ActionController::Parameters no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html (called from new at (pry):1)
NoMethodError: Expected ["0", "foofoo"} permitted: true>] to respond to #to_hash
from /Users/neillocal/.rvm/gems/ruby-2.3.1/gems/virtus-1.0.5/lib/virtus/attribute_set.rb:196:in `coerce'
And then the whole thing errors out with the following message:
Expected ["0", <ActionController::Parameters {"email_text"=>"foofoo"} permitted: true>] to respond to #to_hash
I feel like I am either close and am missing something small in order for it to work, or I am realizing that virtus is outdated and no longer usable (via the deprecation warnings).
Resources I looked at:
this article.
this video
I did attempt to get the same form to work but with the reform-rails gem. I ran into an issue there too. That question is posted here.
Thanks in advance!
I would just set the emails_attributes from user_form_params in the user_form.rb as a setter method. That way you don't have to customize the form fields.
Complete Answer:
Models:
#app/modeles/user.rb
class User < ApplicationRecord
has_many :user_emails
end
#app/modeles/user_email.rb
class UserEmail < ApplicationRecord
# contains the attribute: #email
belongs_to :user
end
Form Objects:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
include Virtus.model
attribute :name, String
validates :name, presence: true
validate :all_emails_valid
attr_accessor :emails
def emails_attributes=(attributes)
#emails ||= []
attributes.each do |_int, email_params|
email = EmailForm.new(email_params)
#emails.push(email)
end
end
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
user = User.new(name: name)
new_emails = emails.map do |email|
UserEmail.new(email: email.email_text)
end
user.user_emails = new_emails
user.save!
end
def all_emails_valid
emails.each do |email_form|
errors.add(:base, "Email Must Be Present") unless email_form.valid?
end
throw(:abort) if errors.any?
end
end
# app/forms/email_form.rb
# "Embedded Value" Form Object. Utilized within the user_form object.
class EmailForm
include ActiveModel::Model
include Virtus.model
attribute :email_text, String
validates :email_text, presence: true
end
Controller:
# app/users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user_form = UserForm.new
#user_form.emails = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
#user_form = UserForm.new(user_form_params)
if #user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {emails_attributes: [:email_text]})
end
end
Views:
#app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: #user_form %>
#app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this User from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<%= f.fields_for :emails do |email_form| %>
<div class="field">
<%= email_form.label :email_text %>
<%= email_form.text_field :email_text %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You have an issue because you haven't whitelisted any attributes under :emails. This is confusing, but this wonderful tip from Pat Shaughnessy should help set you straight.
This is what you're looking for, though:
params.require(:user_form).permit(:name, { emails: [:email_text, :id] })
Note the id attribute: it's important for updating the records. You'll need to be sure you account for that case in your form objects.
If all this form object malarkey with Virtus gets to be too much, consider Reform. It has a similar approach, but its raison d'etre is decoupling forms from models.
You also have an issue with your form… I'm not sure what you were hoping to achieve with the syntax you're using, but if you look at your HTML you'll see that your input names aren't going to pan out. Try something more traditional instead:
<%= f.fields_for :emails do |ff| %>
<%= ff.text_field :email_text %>
<% end %>
With this you'll get names like user_form[emails][][email_text], which Rails will conveniently slice and dice into something like this:
user_form: {
emails: [
{ email_text: '...', id: '...' },
{ ... }
]
}
Which you can whitelist with the above solution.
The problem is that the format of the JSON being passed to UserForm.new() is not what is expected.
The JSON that you are passing to it, in the user_form_params variable, currently has this format:
{
"name":"testform",
"emails":{
"0":{
"email_text":"email1#test.com"
},
"1":{
"email_text":"email2#test.com"
},
"2":{
"email_text":"email3#test.com"
}
}
}
UserForm.new() is actually expecting the data in this format:
{
"name":"testform",
"emails":[
{"email_text":"email1#test.com"},
{"email_text":"email2#test.com"},
{"email_text":"email3#test.com"}
}
}
You need to change the format of the JSON, before passing it to UserForm.new(). If you change your create method to the following, you won't see that error anymore.
def create
emails = []
user_form_params[:emails].each_with_index do |email, i|
emails.push({"email_text": email[1][:email_text]})
end
#user_form = UserForm.new(name: user_form_params[:name], emails: emails)
if #user_form.save
redirect_to #user, notice: 'User was successfully created.'
else
render :new
end
end
The example code below is a contrived example of an attempt at a form object where it is probably overkill to utilize a form object. Nonetheless: it shows the issue I am having:
I have two models: a User and an Email:
# app/models/user.rb
class User < ApplicationRecord
has_many :emails
end
# app/models/user.rb
class Email < ApplicationRecord
belongs_to :user
end
I want to create a form object which creates a user record, and then creates three associated email records.
Here are my form object classes:
# app/forms/user_form.rb
class UserForm
include ActiveModel::Model
attr_accessor :name, :email_forms
validates :name, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
puts "I would proceed to create all the necessary objects by hand"
user = User.create(name: name)
email_forms.each do |email|
Email.create(user: user, email_text: email.email_text)
end
end
end
# app/forms/email_form.rb
class EmailForm
include ActiveModel::Model
attr_accessor :email_text, :user_id
validates :email_text, presence: true
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
puts "The Form is VALID!"
# DON'T THINK I WOULD PERSIST DATA HERE
# INSTEAD DO IT IN THE user_form
end
end
Notice: the validations on the form objects. A user_form is considered to be invalid if it's name attribute is blank, or if the email_text attribute is left blank for any of the email_form objects inside it's email_forms array.
For brevity: I will just be going through the new and create action of utilizing the user_form:
# app/controllers/user_controller.rb
class UsersController < ApplicationController
def new
#user_form = UserForm.new
#user_form.email_forms = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
#user_form = UserForm.new(user_form_params)
if #user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {email_forms: [:_destroy, :id, :email_text, :user_id]})
end
end
Lastly: the form itself:
# app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: #user_form %>
<%= link_to 'Back', users_path %>
# app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
# MESSY, but couldn't think of a better way to do this...
<% unique_index = 0 %>
<% user_form.email_forms.each do |email_form| %>
<div class="field">
<%= label_tag "user_form[email_forms][#{unique_index}][email_text]", "Email Text" %>
<%= text_field_tag "user_form[email_forms][#{unique_index}][email_text]" %>
</div>
<% unique_index += 1 %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The form does render:
And here is the form's html:
I go to submit the form. Here is the params hash:
Parameters: {"utf8"=>"✓", "authenticity_token"=>”abc123==", "user_form"=>{"name"=>"neil", "email_forms"=>{"0"=>{"email_text"=>"test_email_1"}, "1"=>{"email_text"=>"test_email_2"}, "2"=>{"email_text"=>""}}}, "commit"=>"Create User form"}
What should happen is the form should be re-rendered and nothing persisted because the form_object is invalid: All three associated emails must NOT be blank. However: the form_object thinks it is valid, and it blows up in the persist! method on the UserForm. It highlights the Email.create(user: user, email_text: email.email_text) line and says:
undefined method `email_text' for ["0", {"email_text"=>"test_email_1"}]:Array
Clearly there are a couple things going on: The nested validations appear to not be working, and I am having trouble rebuilding each of the emails from the params hash.
Resources I have already examined:
This Article seemed promising but I was having trouble getting it to work.
I have attempted an implementation with the virtus gem and the reform-rails gem. I have pending questions posted for both of those implementations as well: virtus attempt here and then reform-rails attempt here.
I have attempted plugging in accepts_nested_attributes, but was having trouble figuring out how to utilize that with a form object, as well as a nested form object (like in this code example). Part of the issue was that has_many and accepts_nested_attributes_for do not appear to be included in ActiveModel::Model.
Any guidance on getting this form object to do what is expected would be very much appreciated! Thanks!
Complete Answer
Models:
#app/models/user.rb
class User < ApplicationRecord
has_many :emails
end
#app/models/email.rb
class Email < ApplicationRecord
belongs_to :user
end
Controller:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user_form = UserForm.new
#user_form.emails = [EmailForm.new, EmailForm.new, EmailForm.new]
end
def create
#user_form = UserForm.new(user_form_params)
if #user_form.save
redirect_to users_path, notice: 'User was successfully created.'
else
render :new
end
end
private
def user_form_params
params.require(:user_form).permit(:name, {emails_attributes: [:email_text]})
end
end
Form Objects:
#app/forms/user_form.rb
class UserForm
include ActiveModel::Model
attr_accessor :name, :emails
validates :name, presence: true
validate :all_emails_valid
def emails_attributes=(attributes)
#emails ||= []
attributes.each do |_int, email_params|
email = EmailForm.new(email_params)
#emails.push(email)
end
end
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
user = User.new(name: name)
new_emails = emails.map do |email_form|
Email.new(email_text: email_form.email_text)
end
user.emails = new_emails
user.save!
end
def all_emails_valid
emails.each do |email_form|
errors.add(:base, "Email Must Be Present") unless email_form.valid?
end
throw(:abort) if errors.any?
end
end
app/forms/email_form.rb
class EmailForm
include ActiveModel::Model
attr_accessor :email_text, :user_id
validates :email_text, presence: true
end
Views:
app/views/users/new.html.erb
<h1>New User</h1>
<%= render 'form', user_form: #user_form %>
<%= link_to 'Back', users_path %>
#app/views/users/_form.html.erb
<%= form_for(user_form, url: users_path) do |f| %>
<% if user_form.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user_form.errors.count, "error") %> prohibited this User from being saved:</h2>
<ul>
<% user_form.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<%= f.fields_for :emails do |email_form| %>
<div class="field">
<%= email_form.label :email_text %>
<%= email_form.text_field :email_text %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to update an invoice fields, when checking out in the carts controller. These must be present when checking out, or it should fail. However, I can't get it to update, much less validate them.
Here is my code:
cart show view:
<div class = "row">
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Customer: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Seller: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<%= submit_tag 'Complete', class: 'btn btn-success' %>
<% end %>
</div>
</div>
carts controller:
class CartsController < ApplicationController
def show
#invoice = current_invoice
#invoice_products = current_invoice.invoice_products
#customers = Customer.all
#employees = Employee.all
end
def checkout
current_invoice.customer_id = params[:customer_id]
current_invoice.employee_id = params[:employee_id]
current_invoice.save
redirect_to current_invoice
end
end
current_invoice is the current session's invoice, related to the cart. It redirects correctly, but doesn't update.
in the invoices controller:
def invoice_params
params.require(:invoice).permit(:invoice_number, :customer_id, :invoice_date, :invoice_status_id, :employee_id, invoice_products_attributes: [:id, :invoice_id, :product_id, :price, :tax, :discount, :value])
end
Can anyone please help me in identifying where I am going wrong? Could it be my approach is not even valid?
Thanks in advance
The type of functionality you're after is considered "business logic" and should be implemented in the model and called from the controller.
You can define a method in a model:
class Invoice < ActiveRecord::Base
def update_invoice(cust_id, emp_id)
if self.update_attributes(:customer_id => cust_id], :employee_id = emp_id])
puts "Success!
else
puts "Failed to update record. Handle the error."
end
end
You can call my_method from carts_controller.rb like this:
def update
# all your regular update logic here
# replace the bit of code that saves the cart with something like this:
respond_to do |format|
if(current_invoice.update_invoice(params[:customer_id], params[:employee_id])
if(#cart.update(cart_params))
format.html { redirect_to #activity, notice: 'Activity was successfully updated.' }
format.json { render :show, status: :ok, location: #activity }
else
format.html { render :edit }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
Also, note the use of update_attributes rather than save. Bear in mind that update_attributes will return false if you run into any problems updating (e.g. one or more validations failed). Don't confuse update_attributes with the singular update_attribute which updates a single field and will not run validations.
Finally got it.
current_invoice.update_attributes(customer_id: params[:invoice][:customer_id], employee_id: params[:invoice][:employee_id])
Also in view, changed location of form_tag:
<div class = "row">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Cliente: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Vendedor: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= submit_tag 'Completar', class: 'btn btn-success' %>
</div>
<% end %>
</div>
Could it be my approach is not even valid
Your approach is definitely valid, it's great that you're using sessions in this way.
I'd do it slightly differently:
#config/routes.rb
resource :cart, except: [:edit, :new, :create], path_names: { update: "checkout" }
This will give you the following paths:
#url.com/cart -> carts#show (here you can invoke a cart if one doesn't exist)
#url.com/cart/checkout #-> POST to "update" method in carts controller
#url.com/cart/ (method: :delete) -> DELETE to "destroy" cart (refresh)
--
#app/controllers/carts_controller.rb
class CartsController < ApplicationController
before_action :setup_cart
def show
#cart = current_cart #-> products called from this. I don't know how you're linking them
#customers = Customer.all
#employees = Employee.all
end
def update
#invoice = Invoice.find_or_create_by(id: current_card.id)
#invoice.update update_params
redirect_to cart_path
end
def destroy
current_cart = nil
redirect_to carts_path, notice: "Cart Cleared"
end
private
def setup_cart
current_cart ||= sessions[:cart]
end
def update_params
params.require(:cart).permit(:customer_id, :employee_id)
end
end
Now, to update the cart, you'll want to take note from MarsAtomic's answer. However it must be noted that naked params are not available in the model.
If you use update_attributes, or just plain update, you'll need to do the following:
#app/models/cart.rb
class Invoice < ActiveRecord::Base
has_many :products
belongs_to :employee
belongs_to :customer
#validations here
#callbacks here (what MarsAtomic refers to as business logic)
before_save :do_something, only: :update
private
def do_something
#something here
#params appended to current instance of object
#eg self.customer_id
end
end
I'd also go more succinct in your view:
#app/views/carts/show.html.erb
<div class = "row">
<%= form_tag cart_checkout_path, method: :patch do |f| %>
<% options = [["cliente", "customer"], ["vendedor", "employee"]] %>
<% options.each do |name, type| %>
<%= content_tag :strong, "#{name.titleize}:" %>
<%= collection_select :cart, eval(":#{type}_id"), instance_variable_get("##{type.pluralize}"), :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
<% end %>
<% content_tag :div, class: "col-lg-12 text-right" do %>
<%= submit_tag 'Completar', class: 'btn btn-success' %>
<% end %>
<% end %>
</div>
Hi I'm adding voting to my site. Users can vote Yes or No. I made the form but it returns an error, only for the Yes button
<%= form_for([#post, #post.votes.build]) do |c| %>
<%= c.input value: 1, type: :hidden %>
<%= c.submit :Yes, class: "btn btn-large btn-primary" %>
<% end %>
This is error: We're sorry, but something went wrong.
In theory I want to make something like this form:
<div class="text-success">
Votează inițiativa<br>
<button class="btn btn-large btn-primary">PRO</button> sau
<button class="btn btn-danger">CONTRA</button>
</div>
See my controller:
class VotesController < ApplicationController
def new
end
def create
#post = Post.find(params[:id])
#votes = #post.votes.create(votes_params)
#votes.user = current_user
#votes.save
redirect_to #votes
end
private
def votes_params
votes_params = params.require(:votes).permit(:stare)
end
end
See my model:
class Vote < ActiveRecord::Base
belongs_to :user, dependent: :destroy
belongs_to :post
end
How do I do this?
You'll need to post your actual error -- generally by typing heroku logs (sounds like you've got it running in production)
From what I can see, I'd try fixing the error like this:
<%= form_for([#post, #post.votes.build]) do |c| %>
<%= c.input value: 1, type: :hidden %>
<%= c.submit :Yes, class: "btn btn-large btn-primary" %>
<% end %>
def create
#post = Post.find(params[:id])
#votes = #post.votes.create(votes_params)
#votes.save
redirect_to #post
end
private
def votes_params
params.require(:post).permit(:stare).merge(user_id: current_user.id)
end
I think your params hash will be somewhat malformed - you'd need to post it to help us appreciate how to fix the error properly