I am having trouble saving data using a nested form with the cocoon gem, and haven't been able to find a solution on SO.
I have two models: Requests has_many Votes.
I would like to create a single form that saves a new request, and a new vote simultaneously. The issue is that currently neither a new request nor a new vote is saved using the below code. I've pasted the terminal output below for completeness.
Terminal output:
Started POST "/requests" for ::1 at 2015-06-25 15:41:17 +0100
Processing by RequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"FCHcO1RLg2zr6mo8MpmVNMEasvPrAqyHAY5d2SdrlDUn83ppnsRhQUU33YiOZ84c4z1xMYQI4gzjLymN1NL3fw==", "request"=>{"firstname"=>"sdf", "lastname"=>"sf", "email"=>"sdf#lskdjf.com", "request"=>"lskdj", "description"=>"sldkjf", "votes_attributes"=>{"1435243272926"=>{"comment"=>"sadflkj", "email"=>"lkfj#sldk.com", "beta"=>"1", "_destroy"=>""}}}, "commit"=>"Save"}
(0.2ms) begin transaction
Vote Exists (0.2ms) SELECT 1 AS one FROM "votes" WHERE LOWER("votes"."email") = LOWER('lkfj#sldk.com') LIMIT 1
Request Exists (0.1ms) SELECT 1 AS one FROM "requests" WHERE LOWER("requests"."email") = LOWER('sdf#lskdjf.com') LIMIT 1
(0.1ms) rollback transaction
Rendered requests/_vote_fields.html.haml (0.9ms)
Rendered requests/_vote_fields.html.haml (1.0ms)
Rendered requests/_form.html.haml (8.4ms)
Rendered requests/new.html.erb within layouts/application (9.2ms)
Completed 200 OK in 118ms (Views: 105.8ms | ActiveRecord: 0.5ms)
Models:
class Request < ActiveRecord::Base
has_many :votes, dependent: :destroy
accepts_nested_attributes_for :votes
end
class Vote < ActiveRecord::Base
belongs_to :request
end
Requests controller
class RequestsController < ApplicationController
before_action :set_request, only: [:show, :edit, :update, :destroy]
def create
#request = Request.new(request_params)
if #request.save
redirect_to #request
else
render 'new'
end
end
private
def set_request
#request = Request.find(params[:id])
end
def request_params
params.require(:request).permit(
:id, :firstname, :lastname, :email, :request, :description, :votes, :tag_list,
votes_attributes: [:firstname, :lastname, :email, :comment, :beta, :id, :_destroy] )
end
end
requests/_form.html
= form_for(#request) do |f|
.field= f.text_field :firstname, placeholder: "Requester firstname"
.field= f.text_field :lastname, placeholder: "Requester Lastname"
.field= f.text_field :email, placeholder: "Email"
.field= f.text_field :request, placeholder: "Request"
.field= f.text_field :description, placeholder: "Description"
#votes
= f.fields_for :votes do |vote|
= render 'votes/vote_fields', :f => vote
.links
= link_to_add_association 'add vote', f, :votes, :render_options => { :wrapper => 'inline' }, partial: 'votes/vote_fields'
.action= f.submit "Save"
requests/_votes_fields.html.haml
.nested-fields
.form-inline
= f.text_field :comment, :placeholder => 'comment'
= f.text_field :email, :placeholder => 'email'
= f.check_box :beta, :placeholder => 'beta', :as => :boolean
.links
= link_to_remove_association "remove vote", f
votes and model validations:
vote.rb
belongs_to :request
before_save { self.email = email.downcase } #to ensure email uniqueness
validates :email, presence: true, length: { maximum: 250 }, uniqueness: { case_sensitive: false }
validates :request_id, presence: :true
request.rb
belongs_to :user
has_many :votes, dependent: :destroy
accepts_nested_attributes_for :votes
before_save { self.email = email.downcase }
validates :request, presence: true
validates :firstname, presence: true, length: { maximum: 50 }
validates :lastname, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 250 }, uniqueness: { case_sensitive: false }
acts_as_taggable
Related
I am creating an exercise tracking website that has a Workout table, a WorkoutExercise table, and an Exercise table (this is just some of the schema).
I have been trying to create a "WorkoutExercise" from the Edit / create Workout form. I'm using Formtastic and my form displays the list of exercises for the user, but when I submit the form, no WorkoutExercises get created, but the workout does get created or updated.
I've searched google and stackoverflow for this issue for a day and a half with not much help. From what I can tell, this should "just work", I think I've missed something simple. This is my first RoR website, so please bear with me:)
Another thing to note, if I manually create the relationship in PSQL, the exercises that I create a relation for show up checked, but when I hit submit, the WorkoutExercise records are deleted from the database.
Database Schema for these 3 tables:
create_table "exercises", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.text "notes"
t.decimal "base_weight"
end
create_table "workout_exercises", force: true do |t|
t.integer "workout_id"
t.integer "exercise_id"
t.integer "exercise_order"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "workouts", force: true do |t|
t.integer "user_id"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
end
Exercise Model:
class Exercise < ActiveRecord::Base
has_many :weight_logs, dependent: :destroy
belongs_to :user
has_many :workout_exercises
has_many :workouts, :through => :workout_exercises
...
WorkoutExercise Model (Many to many table)
class WorkoutExercise < ActiveRecord::Base
belongs_to :exercise
belongs_to :workout
accepts_nested_attributes_for :workout
#default_scope -> { order('exercise_order ASC') }
validates :exercise_id, presence: true
validates :workout_id, presence: true
validates :exercise_order, presence: true
end
Workout Model:
class Workout < ActiveRecord::Base
has_many :workout_exercises
has_many :exercises, :through => :workout_exercises, :class_name => 'Exercise'
belongs_to :user
accepts_nested_attributes_for :exercises
accepts_nested_attributes_for :workout_exercises
...
Workouts_conroller.rb
class WorkoutsController < ApplicationController
before_action :set_workout, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :correct_user, only: :destroy
# GET /workouts
# GET /workouts.json
def index
#workouts = Workout.all
end
# GET /workouts/1
# GET /workouts/1.json
def show
end
def add_exercise
end
# GET /workouts/new
def new
#workout = current_user.workouts.new
#exercises = current_user.exercises.all
end
# GET /workouts/1/edit
def edit
#exercises = #workout.exercises.build
end
# POST /workouts
# POST /workouts.json
def create
#workout = current_user.workouts.build(workout_params)
respond_to do |format|
if #workout.save
format.html { redirect_to #workout, notice: 'Workout was successfully created.' }
format.json { render :show, status: :created, location: #workout }
else
format.html { render :new }
format.json { render json: #workout.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /workouts/1
# PATCH/PUT /workouts/1.json
def update
respond_to do |format|
if #workout.update(workout_params)
format.html { redirect_to #workout, notice: 'Workout was successfully updated.' }
format.json { render :show, status: :ok, location: #workout }
else
format.html { render :edit }
format.json { render json: #workout.errors, status: :unprocessable_entity }
end
end
end
# DELETE /workouts/1
# DELETE /workouts/1.json
def destroy
#workout.destroy
respond_to do |format|
format.html { redirect_to workouts_url, notice: 'Workout was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_workout
#workout = Workout.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def workout_params
params.require(:workout).permit(:description, :name,
:exercise_ids => [:exercise_id])
end
def correct_user
#workout = current_user.workouts.find_by(id: params[:id])
redirect_to root_url if #workout.nil?
end
end
Rails Server log on Save:
Started PATCH "/workouts/6" for 127.0.0.1 at 2014-07-13 19:30:05 -0700
Processing by WorkoutsController#update as HTML
Parameters: {"utf8"=>"v", "authenticity_token"=>"WY2P1Z7/g/PoG5iB6VufDNOG7Hyk7tnJQzTT00CpNTM=", "workout"=>{"name"=>"sad", "description"=>"asd", "exercise_ids"=>["", "17", "23",
"9", "15", "14", "18"]}, "commit"=>"Update Workout", "id"=>"6"}
?[1m?[36mWorkout Load (0.0ms)?[0m ?[1mSELECT "workouts".* FROM "workouts" WHERE "workouts"."id" = $1 LIMIT 1?[0m [["id", 6]]
?[1m?[35mUser Load (1.0ms)?[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
?[1m?[36m (0.0ms)?[0m ?[1mBEGIN?[0m
?[1m?[35mExercise Load (0.0ms)?[0m SELECT "exercises".* FROM "exercises" INNER JOIN "workout_exercises" ON "exercises"."id" = "workout_exercises"."exercise_id" WHERE "workout_ex
ercises"."workout_id" = $1 ORDER BY name ASC [["workout_id", 6]]
?[1m?[36m (0.0ms)?[0m ?[1mCOMMIT?[0m
Redirected to http://localhost:3000/workouts/6
And here's my form too:
<%= semantic_form_for #workout do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<%= f.text_area :description %>
<%= f.input :exercises, :as => :check_boxes, :collection => Exercise.where(:user_id => #workout.user_id) %>
<% end%>
<%= f.actions %>
<% end %>
EDIT
I'm unable to answer my question, but I've found a workaround. I don't think this is the best solution, but it works! This was added in the def update of my workouts_controller.rb
if #workout.update(workout_params)
params[:workout].each do |key,value|
if key == 'exercise_ids'
#exercises = Exercise.find(value.reject!{|a| a==""})
#workout.exercises << #exercises
end
end
I'm trying to allow users to sign in with either their username or their email address.
as per here: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign-in-using-their-username-or-email-address
When ever I try to sign in with either the email or the login this is logged:
Started POST "/users/sign_in" for 192.168.2.8 at 2013-11-22 10:11:50 +0800
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"â", "authenticity_token"=>"WVTOKWQ8yJAJSu3NmXi3skJ8UC8zXY7qGZj7qbM3cr0=", "user"=>{"email"=>"testuser", "password"=>"password"}, "commit"=>"Sign in"}
User Load (3.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'testuser' ORDER BY "users"."id" ASC LIMIT 1
Completed 401 Unauthorized in 20ms
Processing by Devise::SessionsController#new as HTML
Parameters: {"utf8"=>"â", "authenticity_token"=>"WVTOKWQ8yJAJSu3NmXi3skJ8UC8zXY7qGZj7qbM3cr0=", "user"=>{"email"=>"rtype", "password"=>"password"}, "commit"=>"Sign in"}
Rendered devise/sessions/new.html.erb within layouts/application (6.4ms)
Completed 200 OK in 80ms (Views: 62.6ms | ActiveRecord: 0.0ms)
its weird the sql isn't checking for the username, so maybe the call on the user is wrong, but its exactly from the devise documentation.
I have read the following:
Adding Username to devise rails 4
Logging in with username using Devise with Rails 4
I also reviewed the warden code and the devise code to try and discover if I'm overriding the wrong method in the user.
Any ideas why it is not authenticating with username, when it does with email?
ApplicationController.rb
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :remember_me) }
# Need to add other user fields like gender etc.
devise_parameter_sanitizer.for(:account_update) {|u| u.permit(:email, :password, :password_confirmation, :current_password)}
end
end
User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :encryptable, :recoverable, :rememberable, :trackable, :validatable
attr_accessor :login # This is used to add the login for allowing user to sign in with both email or username
# Validations
validates :username, presence: true, length: {maximum: 255}, uniqueness: { case_sensitive: false }, format: { with: /\A[a-zA-Z0-9]*\z/, message: "may only contain letters and numbers." }
# Overwrite devise’s find_for_database_authentication method in user model
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:username)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
end
new.html
<fieldset>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => 'form-horizontal' }) do |f| %>
<div class="control-group">
<%= f.label :login, "Email or Username" %>
<div class="controls"><%= f.text_field :login, :autofocus => true %></div>
</div>
<div class="control-group">
<%= f.label :password %>
<div class="controls"><%= f.password_field :password %></div>
</div>
<div class="form-actions">
<%= f.submit "Sign in", :class => "btn btn-primary", data: { disable_with: 'Working...' } %> <%= link_to "Forgot password?", new_password_path(resource_name), :class => "btn" %>
</div>
<% end %>
</fieldset>
Devise.rb
config.secret_key = 'Omitted for security'
config.mailer_sender = 'Omitted for security'
config.case_insensitive_keys = [ :email ]
config.strip_whitespace_keys = [ :email ]
config.skip_session_storage = [:http_auth]
config.stretches = Rails.env.test? ? 1 : 10
config.reconfirmable = true
config.password_length = 8..128
config.reset_password_within = 6.hours
config.encryptor = :sha512
config.sign_out_via = :delete
You need to add
config.authentication_keys = [ :login ]
within devise.rb
Also if you want to use login as reset password or confirmation keys you should also add within the same file:
config.reset_password_keys = [ :login ]
config.confirmation_keys = [ :login ]
You also need to whitelist the login parameter within your application_controller. In the new.html view you have a login form, so you must accept the login parameter for the sign_in instead of the username and email parameters.
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :password, :remember_me) }
I have the following model:
class Coupon < ActiveRecord::Base
belongs_to :company
validates :description, presence: true, length: { maximum: 50 }, uniqueness: { case_sensitive: false }
validates :fine_print, presence: true
end
and the following method in the coupon controller:
def redeem
if params[:pin] == #coupon.company.pin
redirect_to root_path
else
flash.now[:notice] = "Incorrect Pin"
render :show
end
end
This form is in the a view:
<%= form_for( #coupon, :url => coupons_redeem_path( #coupon ), :html => { :method => :post } ) do |f| %>
<%= label_tag("pin", "Search for:") %>
<%= text_field_tag("pin") %>
<%= f.submit "Close Message" %>
<% end %>
I want the form to call the redeem method in the coupons controller when hitting submit but am getting this error:
No route matches [POST] "/coupons/redeem.1"
EDIT
These are my routes:
resources :companies do
resources :coupons
end
get 'coupons/redeem'
In your routes, coupons are nested resources of companies. So you should choose one of these alternatives:
1st:
resources :companies do
resources :coupons do
post :redeem, on: :member
end
end
This leads to helpers like this: redeem_company_coupon_path(company, coupon) (and send smth there via POST).
If you don't want to include company to your path, you could choose 2nd:
resources :companies do
resources :coupons
end
post 'coupons/:id/redeem', to: 'coupons#redeem', as: :redeem_coupon
After that you could use redeem_coupon_path(coupon) helper
I have this code inside app/admin/products.rb. I have problem with that I can see just one input field at time(the last one listed) or just submit button. Does it could be because of some syntax mistakes ? But there is no any error messages.
ActiveAdmin.register Product do
f.input :name,:label => "Name"
f.input :photo, :as => :file
f.input :category, :collection => #category
f.input :manufacturer, :collection => #manufacturer
f.actions do
f.action :submit, :button_html => { :class => "primary", :disable_with => 'Wait...' }
end
end
end
Product model looks like this
attr_accessible :category_id, :description, :manufacturer_id, :name, :photo
extend FriendlyId
has_attached_file :photo,
:styles => {
:thumb=> "100x100#",
:large => "290x170",
:medium=> "120x120"}
friendly_id :name, use: [:slugged, :history]
belongs_to :manufacturer
belongs_to :category
Your form should be inside the block.
Example
ActiveAdmin.register Post do
form do |f|
f.inputs "Details" do
f.input :title
f.input :published_at, :label => "Publish Post At"
f.input :category
end
f.inputs "Content" do
f.input :body
end
f.actions
end
end
Here is more info
http://activeadmin.info/docs/5-forms.html
My nested model form is working great on the first level deep. But I was under the impression that you could go many levels deep using accepts_nested_attributes_for. But when I try the code below, the "Image" attributes are attached to the top level "Question" model and it breaks upon form submission with an unknown attribute "Image" error.
I could do the inserts all by hand using the form data but if Rails can handle it automatically, it would be better for obvious reasons.
What am I doing wrong? I tried changing |af| in the "fields for :image do" to its own unique name but it didn't have any effect.
Models:
class Question < ActiveRecord::Base
has_one :answer
accepts_nested_attributes_for :answer
end
class Answer < ActiveRecord::Base
belongs_to :question
has_one :image
accepts_nested_attributes_for :image
end
class Image < ActiveRecord::Base
belongs_to :answer
end
Controller:
def new
#question = Question.new
answer = #question.build_answer
image = answer.build_image
#case_id = params[:id]
render :layout => 'application', :template => '/questions/form'
end
def create
question_data = params[:question]
#question = Question.new(question_data)
if #question.save
...
end
View:
= form_for #question, :html => {:multipart => true} do |f|
= f.label :text, "Question Text:"
= f.text_area :text, :rows => 7
%br
%br
=f.fields_for :answer, do |af|
= af.label :body, "Answer Text:"
= af.text_area :body, :rows => 7
%br
%br
= f.fields_for :image do |af|
= af.label :title, "Image Title:"
= af.text_field :title
%br
= af.label :file, "Image File:"
= af.file_field :file
%br
= af.label :caption, "Image Caption:"
= af.text_area :caption, :rows => 7
= hidden_field_tag("case_id", value = #case_id)
= f.submit
I think you've got the form variables slightly mixed up. It should be:
= form_for #question, :html => {:multipart => true} do |f|
= f.label :text, "Question Text:"
= f.text_area :text, :rows => 7
%br
%br
=f.fields_for :answer, do |af|
= af.label :body, "Answer Text:"
= af.text_area :body, :rows => 7
%br
%br
= af.fields_for :image do |img_form|
= img_form.label :title, "Image Title:"
= img_form.text_field :title
%br
= img_form.label :file, "Image File:"
= img_form.file_field :file
%br
= img_form.label :caption, "Image Caption:"
= img_form.text_area :caption, :rows => 7
= hidden_field_tag("case_id", value = #case_id)
= f.submit
Notice how form_for ... do |f| spawns f.fields_for ... do |af|, which in turns spawns af.fields_for ... do |img_form|.
The key is the second fields_for. It should be af.fields_for :image do |img_form| rather than f.fields_for :image do |img_form|.