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
Related
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
I am trying to build a simple application where user can post something and attach questions to the post (post.questions).
I am getting problem displaying view in embeds_many scenario. Though I create multiple questions in PostsController # new method, html after rendering only shows one question.
I am using rails4/mongoid/mongodb.
Post.rb:
class Post
include Mongoid::Document
include Mongoid::Timestamps
field :title, type: String
field :body, type: String
embeds_many :post_replies
embeds_many :questions
belongs_to :poster, class_name: "User"
accepts_nested_attributes_for :questions
end
Quesion.rb.
class Question
include Mongoid::Document
include Mongoid::Timestamps
field :q
embedded_in :post
end
PostsController.rb
class PostsController < ApplicationController
def new
#post = Post.new
3.times{
post.questions.build
}
end
def post_params
params.require(:post).permit(:title, :body, questions_attributes: [:id, :q, :_destroy])
end
end #end controller
Routes.rb
devise_for :users
resources :users do
resources :posts
resources :addresses
end
views/posts/_form.html.erb
<%= form_for([current_user, #post], :html => { :class => "form-horizontal" }) do |f| %>
-
-
<%=f.fields_for #post.questions do|question| %>
<%= question.text_field :q, :placeholder =>'question', :class => "form-control" %>
<%end%>
-
-
<%end%>
I expect above fields for to display 3 questions but it only shows one question. I tried different ways for fields_for but none worked.
Any help in this is greatly appreciated.
Thanks in advance.
I changed
<%=f.fields_for #post.questions do|question| %>
to
<%=f.fields_for :questions, #post.questions do|question| %>
and it worked.
Api ref: here http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
I am trying to nest a form with two resources (Map and Location).
I'm following the Nested Forms tutorial from Ryan Bates however I must be missing something.
Map Model:
module Refinery
module Maps
class Map < Refinery::Core::BaseModel
self.table_name = 'refinery_maps'
attr_accessible :name, :position, :questions_attributes
validates :name, :presence => true, :uniqueness => true
has_many :locations
accepts_nested_attributes_for :locations
end
end
end
Location Model:
module Refinery
module Maps
class Location < Refinery::Core::BaseModel
attr_accessible :latitude, :longitude, :address, :description, :title, :position
validates :address, :presence => true, :uniqueness => true
belongs_to :map
end
end
end
View:
Controller(decorator):
Refinery::PagesController.class_eval do
before_filter :new
private
def new
#map = Map.new
3.times { #map.locations.build }
end
end
Schema:
create_table "refinery_maps_locations", :force => true do |t|
t.float "latitude"
t.float "longitude"
t.string "address"
t.string "description"
t.string "title"
t.integer "position"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "map_id"
end
create_table "refinery_maps", :force => true do |t|
t.string "name"
t.integer "position"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Routes:
Refinery::Core::Engine.routes.draw do
# Frontend routes
namespace :maps do
resources :maps, :path => '', :only => [:index, :show]
end
# Admin routes
namespace :maps, :path => '' do
namespace :admin, :path => Refinery::Core.backend_route do
resources :maps, :except => :show do
collection do
post :update_positions
end
end
end
end
# Frontend routes
namespace :maps do
resources :locations, :only => [:index, :show]
end
# Admin routes
namespace :maps, :path => '' do
namespace :admin, :path => "#{Refinery::Core.backend_route}/maps" do
resources :locations, :except => :show do
collection do
post :update_positions
end
end
end
end
end
You are missing a :locations_attributes in your Map model - you have :questions_attributes there instead.
Also, check out this gem for refinerycms nested models - https://github.com/llamadigital/refinerycms-nested_models
Just replace "nested_models" with the name of the one you want to nest. It works much better with refinery than nested forms.
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
This is basically a resource-less form, but the other examples don't seem to work. I'm using sorcery and formtastic. formtastic is working just fine for the new user form, but i can't seem to create a new session...
<%= semantic_form_for 'sessions', :url => sessions_path, :method => :post do |f| %>
<%= f.inputs do %>
<%= f.input :email %>
<%= f.input :password %>
<% end %>
<%= f.buttons %>
<% end %>
this is close, but it outputs
<input id="sessions_email" name="sessions[email]" type="text" />
instead of
<input id="sessions_email" name="email" type="text" />
so when i submit the form, i get a template missing error
`Template is missing
Missing template sessions/create, application/create with {:handlers=>[:erb, :builder, :coffee], :formats=>[:html], :locale=>[:en, :en]}. `
i guess i just need to know how to set the 'name' attribute of the form specifically, but that seems like a bit of a hack. is there a more 'semantic' way to do this? please help thanks.
EDIT: here's the controller code per request...
class UsersController < ApplicationController
before_filter :authenticate, :except => [:show, :new, :create]
before_filter :correct_user, :only => [:edit, :update]
before_filter :admin_user, :only => :destroy
def index
#title = "All users"
#users = User.paginate(:page => params[:page])
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(:page => params[:page], :per_page => 6)
#title = #user.name
end
def new
#user = User.new
#title = 'Sign up'
end
def create
#user = User.new(params[:user])
if #user.save
Notifier.registration_confirmation(#user).deliver
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
#title = "Sign up"
render 'new'
end
end
def edit
#user = User.find(params[:id])
#title = "Edit user"
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to #user
else
#title = "Edit user"
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User Removed"
redirect_to users_path
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(:page => params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(:page => params[:page])
render 'show_follow'
end
private
def has_valid_signup_code?(code)
code == valid_signup_code
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
thanks!
You are probably missing some kind of redirect after successful login. This is how the create action in my sessions controller looks like (note my controller is named UserSessionsController and not SessionsController):
def create
if #user = login(params[:session][:email], params[:session][:password])
redirect_back_or_to root_path, :notice => I18n.t("user_sessions.login_success")
else
flash.now[:alert] = I18n.t("user_sessions.login_failure")
render :action => :new
end
end
And this is the login form:
= semantic_form_for 'session', :url => user_sessions_path do |f|
= f.inputs do
= f.input :email
= f.input :password
= f.buttons do
= f.commit_button