I have two classes Food and food_category_price.
The association looks something like this:
has_many :food_category_prices, dependent: :destroy
accepts_nested_attributes_for :food_category_prices
The foods controller looks something like this for new, create and edit actions.
def new
#food = current_department.foods.new
#food.food_category_prices.build
end
def edit
#food = current_department.foods.find(params[:id])
end
def create
#food = current_department.foods.create(food_params)
if #food.persisted?
flash[:notice] = "Food was successfully created."
redirect_to foods_path
else
flash[:error] = "Error while adding food"
render :new
end
end
My food parameters looks something like this: (I have whitelisted the nested attributes' id)
def food_params
params.require(:food).permit(:name, :description, :price, :image ,food_category_prices_attributes: [:id,:category_id,:extra_price],:category_ids => [])
end
The nested form for new action of food is:
= form_for #food do |f|
.field.form-group
= f.file_field :image, class: 'd-none'
......
......
// Nested form from here
- current_department.categories.where(category_kind: 2).each do |category|
= f.fields_for :food_category_prices do |g|
= g.label :category_id, category.name + "("+String(category.price)+")"
= g.hidden_field :category_id, value: category.id
= g.label :extra_price, "Extra Price"
= g.number_field :extra_price
The problem:
The code words perfeclty fine while creating new food, and nested food_categoy_prices.
The problem is that, when i am editing the form, the attributes are duplicating. The labels such as extra price and category_id are duplicating.
Related
I'm having trouble with what I feel should be a simple issue. I'm trying to create a basic match score reporting app and I'm trying to create a match and the match players simultaneously.
My models:
class Match < ApplicationRecord
has_many :match_players
accepts_nested_attributes_for :match_players
end
class MatchPlayer < ApplicationRecord
belongs_to :player
belongs_to :match
end
My form:
.container
%h1 Log a completed match
= simple_form_for #match do |f|
= f.input :played_at, html5: true
= f.simple_fields_for :match_player do |mp|
= mp.input :player_id, collection: Player.all, label_method: lambda { |player| player.first_name + " " + player.last_name }
= f.submit "Submit Match Score"
My controller action and params:
def create
#match = Match.new(match_params)
if #match.save
player = MatchPlayer.create(player: current_player, match: #match)
opponent = MatchPlayer.create(player: Player.find(match_params[:match_player_attributes][:player_id], match: #match))
else
render :new
end
end
private
def match_params
params.require(:match).permit(:played_at, match_player_attributes: [:player_id])
end
Right now I'm getting a found unpermitted parameter: :match_player issue. If I change match_player_attributes to match_player in my params then I get unknown attribute 'match_player' for Match. The error is occurring on the first line of the create action (#match = Match.new(match_params))
Any help would be appreciated!
Edit following suggestions:
Controller:
def new
#match = Match.new
#match.match_players.build
end
def create
#match = Match.new(match_params)
if #match.save!
player = MatchPlayer.create(player: current_player, match: #match)
opponent = Player.find(match_params[:match_players_attributes]["0"][:player_id])
opponent_match_player = MatchPlayer.create(player: opponent, match: #match)
redirect_to(root_path, notice: "Success!")
else
render :new
end
end
private
def match_params
params.require(:match).permit(:played_at, match_players_attributes: [:player_id])
end
Form:
= simple_form_for #match do |f|
= f.input :played_at, html5: true
= f.simple_fields_for :match_players do |mp|
= mp.input :player_id, collection: Player.all, label_method: lambda { |player| player.first_name + " " + player.last_name }
= f.submit "Submit Match Score"
Now it's creating 3 match_players, one for the current_player and 2 of the opponent. What's going on?
Looks like the problem in the simple typo,
Try to change:
= f.simple_fields_for :match_player do |mp|
to
= f.simple_fields_for :match_players do |mp|
Also
def match_params
params.require(:match).permit(:played_at, match_player_attributes: [:player_id])
end
to
def match_params
params.require(:match).permit(:played_at, match_players_attributes: [:player_id])
end
Here is wiki with examples
UPD:
From wiki I shared with, you can find this point:
accepts_nested_attributes_for - an ActiveRecord class method that goes
in your model code - it lets you create and update child objects
through the associated parent. An in depth explanation is available in
the ActiveRecord documentation.
It means that you don't need to create opponent manually
opponent = Player.find(match_params[:match_players_attributes]["0"][:player_id])
opponent_match_player = MatchPlayer.create(player: opponent, match: #match)
Because when you send your params with nested attributes:
match_players_attributes: [:player_id] inside of match creation process match_player will be created automatically
I can see in your model, you have:
has_many :match_players
hence, in your controller and in your form, you must use match_players (plural not singular)
Thus, in your controller, you will have:
def match_params
params.require(:match).permit(:played_at, match_players_attributes: [:id, :player_id])
end
And, in your form:
...
= f.simple_fields_for :match_players do |mp|
...
Notice the last s of match_player in form and in controller.
I know this kind of question is already answered multiple times but i seriously unable to figure it out what is causing a problem here, I am having trouble solving this problem. I keep getting the same error when i'm trying to create new registration ( http://localhost:3000/registrations/new?course_id=1 ) :
NoMethodError at /registrations
undefined method `id' for nil:NilClass
Here is my RegistrationsController:
class RegistrationsController < ApplicationController
before_action :set_registration, only: [:show, :edit, :update, :destroy]
def index
#registrations = Registration.all
end
def show
end
def new
#registration = Registration.new
#course = Course.new
#course = Course.find_by id: params["course_id"]
end
def create
#registration = Registration.new registration_params.merge(email: stripe_params["stripeEmail"], card_token: stripe_params["stripeToken"])
raise "Please Check Registration Errors" unless #registration.valid?
#registration.process_payment
#registration.save
redirect_to #registration, notice: 'Registration was successfully created.'
rescue Exception => e
flash[:error] = e.message
render :new
end
protect_from_forgery except: :webhook
def webhook
event = Stripe::Event.retrieve(params["id"])
case event.type
when "invoice.payment_succeeded" #renew subscription
Registration.find_by_customer_id(event.data.object.customer).renew
end
render status: :ok, json: "success"
end
private
def stripe_params
params.permit :stripeEmail, :stripeToken
end
def set_registration
#registration = Registration.find(params[:id])
end
def registration_params
params.require(:registration).permit(:course_id, :full_name, :company, :telephone, :email, :card_token)
end
end
My Registration Model:
class Registration < ActiveRecord::Base
belongs_to :course
def process_payment
customer_data = {email: email, card: card_token}.merge((course.plan.blank?)? {}: {plan: course.plan})
customer = Stripe::Customer.create customer_data
Stripe::Charge.create customer: customer.id,
amount: course.price * 100,
description: course.name,
currency: 'usd'
#Annotate Customer Id when Registration is Created
cusotmer_id = customer.id
end
def renew
update_attibute :end_date, Date.today + 1.month
end
end
Registration New.html.haml File :
%section#course-content
%section#ruby
%section.detailed-syllabus
.wrapper-inside
= form_for #registration, html: { class: "basic-grey" } do |f|
- if #registration.errors.any?
#error_explanation
%h2
= pluralize(#registration.errors.count, "error")
prohibited this registration from being saved:
%ul
- #registration.errors.full_messages.each do |message|
%li= message
.field
= f.hidden_field :course_id, value: #course.id
.field
= f.label :full_name
= f.text_field :full_name
.field
= f.label :company
= f.text_field :company
.field
= f.label :email
= f.text_field :email
.field
= f.label :telephone
= f.text_field :telephone
//‘Stripe.js’ will recognize the card data because we have marked the inputs with ‘data-stripe’ attribute as: number, cvv, exp-month and exp-year.
= javascript_include_tag "https://js.stripe.com/v2/"
:javascript
Stripe.setPublishableKey('#{Rails.application.secrets.stripe_publishable_key}');
= label_tag "Card Number", nil, required: true
.control-group
.controls
= text_field_tag :card_number, nil, class: "input-block-level", "data-stripe" => "number"
= label_tag "Card Verification", nil, required: true
.control-group
.controls
= text_field_tag :card_verification, nil, class: "input-block-level", "data-stripe" => "cvv"
= label_tag "Card Expires", nil, required: true
= select_tag :exp_month, options_for_select(Date::MONTHNAMES.compact.each_with_index.map { |name,i| ["#{i+1} - #{name}", i+1] }), include_blank: false, "data-stripe" => "exp-month", class: "span2"
= select_tag :exp_year, options_for_select((Date.today.year..(Date.today.year+10)).to_a), include_blank: false, "data-stripe" => "exp-year", class: "span1"
.actions
= f.submit "Registration Payment", class: "btn", style: "color: white;background: rgb(242, 118, 73);"
Does anyone know how to assist me in this? Greatly appreciate all the help.
Additional Can anyone please guide me through how to pass id between 2 models like this guy did between 2 models as he's creating a scaffold for one model but passing ID lets him create values for another model too without creating actions for another controller https://github.com/gotealeaf/stripe-basics.git
Edited:
GitHub Repository For This Code
https://github.com/ChiragArya/Stripe_CheckOut_Demo
From your comments, it appears the error is caused by :
#course.id being nil
The way to fix this is to ensure #course is defined properly. You need to do the following:
def new
#registration = Registration.new
#course = Course.find_by id: params["course_id"]
end
The other issue you have here is that your routes should be able to handle courses without having to append them with ?course_id=1:
#config/routes.rb
resources :registrations do
get :course_id, to: "registrations#new" #-> yoururl.com/registrations/:course_id
end
This will still give you the course_id param in the new action; just makes it more Rails.
--
Controller
You also need some structure in your code (you're aiming for fat model, thin controller). It looks like you're coming to Rails as a Ruby dev; you need to appreciate that Rails handles most of the exceptions etc for you.
Specifically, you need to look at how to remove code out of your actions:
def create
#registration = Registration.new registration_params
#registration.process_payment
if #registration.save
redirect_to #registration, notice: 'Registration was successfully created.'
else
# handle error here
end
end
private
def registration_params
params.require(:registration).permit(:course_id, :full_name, :company, :telephone, :email, :card_token).merge(email: stripe_params["stripeEmail"], card_token: stripe_params["stripeToken"])
end
-
`id' for nil:NilClass
Finally, you have to remember this error basically means the variable you're trying to invoke an action for is nil.
Ruby populates nil variables with a NilClass object, thus it's difficult to determine what the error actually is. All it means is that the variable you're trying to call a method on doesn't have the aforementioned method, as Ruby has populated it with the NilClass object.
Try changing Registration#new action to
def new
#course = Course.find(params[:course_id])
#registration = #course.registrations.new
end
add this in your def create
def create
#course = Course.find_by id: params["registration"]["course_id"]
#registration = Registration.new registration_params.merge(email: stripe_params["stripeEmail"], card_token: stripe_params["stripeToken"])
raise "Please Check Registration Errors" unless #registration.valid?
#registration.process_payment
#registration.save
redirect_to #registration, notice: 'Registration was successfully created.'
rescue Exception => e
flash[:error] = e.message
#course = Course.find_by id: params["registration"]["course_id"]
render :new
end
Hi I'm trying to create an event depending on the eventable type. the eventable type is either group or shop.
Writing my code, I'm currently sure that their is a better way create my routes and controller (still a rails newbie)
Is there a way to create only one new and create method pass in the eventable type ?
Models:
class Event < ActiveRecord::Base
belongs_to :eventable, polymorphic: true
class Group < ActiveRecord::Base
has_many :events, as: :eventable
class Shop < ActiveRecord::Base
has_many :events, as: :eventable
Routes:
resources :events do
collection do
get :new_national_event
get :new_local_event
post :create_national_event
post :create_local_event
end
do
event-controller:
def index
#search = Search.new(params[:search])
#shop = find_user_shop(#search.shop_id)
#group = #shop.group
#shop_events = #shop.events
#group_events = #group.events
end
def new_national_event
#user = current_user
#event = #user.group.events.new
end
def new_local_event
#shop = find_user_shop(#search.shop_id)
#event = #shop.events.new
end
def create_national_event
user = current_user
#event = user.group.events
if #event.save!
flash.now[:notice] = "Votre événement national a bien été enregistré"
render :index
else
flash.now[:error] = "Erreur lors de l'enregistrement du événement national"
render :new
end
end
def create_local_event
user = current_user
#event = user.group.shop.events
if #event.save!
flash.now[:notice] = "Votre événement local a bien été enregistré"
render :index
else
flash.now[:error] = "Erreur lors de l'enregistrement du événement local"
render :new
end
end
views:
index.html.slim
= link_to new_national_event_events_path
= link_to new_local_event_events_path
new_national_event_events_path.html.slim
= form_for #event, :url => create_national_event_events_path, :method => :post do |f|
div class="field"
= f.text_field :title, :required => true
div class="field"
= f.text_field :threshold, :required => true
div class="form-actions"
=f.submit "Create", class: "btn blue"
If it is just the routing you are concerned with, and not the number of actions, you can use constraints to allow a single path variable send the request to one of a multiple number of actions, this can be useful in some cases where you may want to have multiple buttons to multiple actions all reading from a single form, or if you just want to simplify your routes variable naming.
use the commit_param_routing gem and in your routes file you can write something like:
resources :events do
collection do
post :save, constraints: CommitParamRouting.new(EventController::CREATENATIONAL), action: :create_national_event
post :save, constraints: CommitParamRouting.new(EventController::CREATELOCAL), action: :create_local_event
end
end
add the constants to your controller:
class EventController
CREATENATIONAL = "create national"
CREATELOCAL = "create local"
.....
end
and then all that is left is to add them to your view file submit buttons:
div class="form-actions"
.row
.col-xs-2
=f.submit EventController::CREATENATIONAL
.col-xs-2
=f.submit EventController::CREATELOCAL
sorry for if its not quite what you were looking for or unclear, my first answer!
The context is as follows, I have entities that can have multiple roles. These roles are manageable by the user.
For example, Entity named "Lipsum" may be "Cashier and Salesperson". So, this is a relation many_to_many.
So I have my 3 models: Entity, type_entity and entity_by_type
class Entity < ActiveRecord::Base
has_many :entity_by_types
has_many :type_entities, :through => :entity_by_types
accepts_nested_attributes_for :entity_by_types
end
class EntityByType < ActiveRecord::Base
belongs_to :entity
belongs_to :type_entity
end
class TypeEntity < ActiveRecord::Base
has_many :entity_by_types
has_many :entities, :through => :entity_by_types
end
I have an ordinary CRUD for entity types.
Now, in the CRUD of entities, I have a field Select-Option Multiple. In which the user chooses has 1 or more types, the entity that is creating.
Then my Controller Entity is as follows:
class Logistics::EntitiesController < ApplicationController
def index
#type_entities = TypeEntity.all
render layout: false
# I use this for show All entities by TypeEntity in my view index
end
def show
end
def new
#type_entities = TypeEntity.all
#entity = Entity.new
render layout: false
end
def create
entity = Entity.new(entity_parameters)
if entity.save
flash[:notice] = "Succesfull!."
redirect_to :action => :index
else
flash[:error] = "Error."
redirect_to :action => :index
end
end
def edit
#entity = Entity.find(params[:id])
#type_entities = TypeEntity.all
#action = 'edit'
render layout: false
end
def update
entity = Entity.find(params[:id])
entity.update_attributes(entity_parameters)
flash[:notice] = "Succesfull."
redirect_to :action => :index
end
def destroy
#entity = Entity.destroy(params[:id])
render :json => #entity
end
private
def entity_parameters
params.require(:entity).permit(:name, :surname, entity_by_types_attributes: [:id, :entity_id, :type_entity_id])
end
end
And my partial form (for method create and Update) is:
= simple_form_for([:namespace, #entity], html: {class: 'form-horizontal' }) do |f|
= f.input :name, placeholder: "Nombre", input_html: { class: 'form-control' }, label: false
= f.input :surname, placeholder: "Apellidos", input_html: { class: 'form-control' }, label: false
%select.select2#type-entity-select{:name => "entity[entity_by_types_attributes][type_entity_id][]", :style => "width:100%;padding: 0;border: none;", :multiple => true}
- #type_entities.each do |tent|
%option{value: "#{tent.id}"}
= tent.name
But, when I click in button submit, and "type_entity_id" have 1 or more values; in my database only display a 1 record where, entity_id is OK, however type_entity_id is NULL.
Moreover only view a 1 record, when should see 1 or more records, depending on the number of types of choice in the form.
The problem here is the way of pass type_entity_id in form of array. So, How I can do that?
P.D
The following is how the params go to my controller:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"ASD"1231+Dssr6mRJcXKh9xHDvuVDmVl4jnwIilRBsuE=", "entity"=>{"name"=>"Lorem", "surname"=>"Ipsum", "entity_by_types_attributes"=>{"type_entity_id"=>["1", "4"]}}}
Try this:
def entity_parameters
params.require(:entity).permit(:name, :surname, entity_by_types_attributes: [:id, :entity_id, {:type_entity_id => []}])
end
Edit:
In your form and in def entity_parameters replace type_entity_id with type_entity_ids
Thus, the parameter will refer to a set (array) not to a single object. These are the generic method syntaxes:
Model.associate_id = some integer
Model.associate_ids = an array (for a has_many relation)
I'm using an API instead of a database, so I'm not using ActiveRecord but ActiveModel (I mostly did like here: railscasts.com/episodes/219-active-model)
Thing is, when I try to edit an item (in my case a parking), the action of the form still remains the action of the create and not update.
so when I go on /parkings/2/edit to edit a parking, the form is still:
<form accept-charset="UTF-8" action="/parkings" class="form-horizontal" id="new_parking" method="post">
when it should be more like that with the put hidden field and the parkings/2 as the action:
<form accept-charset="UTF-8" action="/parkings/2" class="form-horizontal" id="edit_parking" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="_method" type="hidden" value="put" />
Anybody knows where the method & action of the form_for is set according to the route? What I'm trying to do is be as close as if I was using ActiveRecord with a database.
Here is some code :
_form.html.erb
<%= form_for(#parking, :html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>
edit.html.erb & new.html.erb, simply has
<%= render 'form' %>
Controller
class ParkingsController < ApplicationController
def index
#parkings = Parse.get("Parking")
respond_to do |format|
format.html
format.json { render :json => #parking }
end
end
def new
#parking = Parking.new
respond_to do |format|
format.html
format.json { render :json => #parking }
end
end
def edit
#parking = Parking.find(params[:id])
respond_to do |format|
format.html
format.json { render :json => #parking }
end
end
def create
#parking = Parking.new(params[:parking])
if (#parking.save)
flash[:success] = "Parking was just added!"
redirect_to :action => "new"
else
render :action => "new"
end
end
def update
# Testing
parking = Parse.get("Parking", params[:id])
parking.delete("updatedAt")
parking["name"] = params[:parking][:name]
parking.save
redirect_to :action => "index"
end
Model
class Parking
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
validates_presence_of :name, :address, :city, :longitude, :latitude
#id = nil
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.find(id)
#id = id
raw = Parse.get("Parking", #id.to_s)
parking = Parking.new
parking.name = raw["name"]
parking.address = raw["address"]
parking.city = raw["city"]
parking.longitude = raw["location"]["longitude"]
parking.latitude = raw["location"]["latitude"]
parking.contributor_name = raw["contributorName"]
parking.contributor_email = raw["contributorEmail"]
return parking
end
def save
if (!valid?)
return false
else
parking = Parse::Object.new("Parking")
data =
{
:longitude => longitude.to_f,
:latitude => latitude.to_f
}
point = Parse::GeoPoint.new(data)
parking["location"] = point
parking["name"] = name
parking["address"] = address
parking["city"] = city
parking["contributorName"] = contributor_name
parking["contributorEmail"] = contributor_email
if (parking.save)
return true
end
end
end
def persisted?
false
end
end
Please note that the create is working and if I add the id of my parking in the form action="" using the Web Inspector or Firebug, and add :method => "put" in my form_for, my record successfully update.
The real problem here is really the form_for action & method who doesn't get updated when I'm editing a parking and remains like if I was adding a new one.
I'm still learning Rails, so sorry if some infos aren't clear!
Thank you!
--- SOLUTION ---
persisted? shouldn't only return false, and my model needed to define a method that returns the id of the object (so they can update the action="") so here's is my updated model:
class Parking
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :objectId, :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
validates_presence_of :name, :address, :city, :longitude, :latitude
#id = nil
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.find(id)
raw = Parse.get("Parking", id.to_s)
parking = Parking.new
parking.objectId = id
parking.name = raw["name"]
parking.address = raw["address"]
parking.city = raw["city"]
parking.longitude = raw["location"]["longitude"]
parking.latitude = raw["location"]["latitude"]
parking.contributor_name = raw["contributorName"]
parking.contributor_email = raw["contributorEmail"]
return parking
end
def save
if (!valid?)
return false
else
parking = Parse::Object.new("Parking")
data =
{
:longitude => longitude.to_f,
:latitude => latitude.to_f
}
point = Parse::GeoPoint.new(data)
parking["location"] = point
parking["name"] = name
parking["address"] = address
parking["city"] = city
parking["contributorName"] = contributor_name
parking["contributorEmail"] = contributor_email
if (parking.save)
return true
end
end
end
def update_attributes(aParking)
parking = Parse.get("Parking", #id.to_s)
parking.delete("updatedAt")
parking["name"] = aParking["name"]
parking.save
return true
end
def destroy
parking = Parse.get("Parking", #id)
#parking.parse_delete
end
def id
return self.objectId
end
def persisted?
!(self.id.nil?)
end
end
I think your problem is in your model's persisted? method. Since it always returns false, Rails always thinks it's building a form for a newly created record, so it uses POST and submits to the collection URL.
You need some sort of logic in that method so that existing records return true and new records return false.
Hi friend you can to tell the form builder which method to use.So try
<%= form_for(#parking, :method => ["new", "create"].include?(action_name) ? :post : :put,
:html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>
If you are not using ActiveRecord you should use 'form_tag' instead 'form_for'