?? Rails 4.0 submiting range_field does not update params - ruby-on-rails

I am thankful to be in the presence of experts
I am trying to use range_field sliders to update some user params/integers.
When I submit the form, the params update temporarily in a <%= #user.risk %> text field I have off to the side, but do not save to the database, when I reload the changes are gone. I am drawing heavily from Michael Hartl's Rails 4.0 Tutorial.
Below I initially used form_for(#user) but current_user seems to work better overall.
`
<%= form_for(current_user) do |f|%>
<%= f.range_field :risk, :in=>0..100, :id=>"slider1"%>
<%= f.range_field :tax, :in=>0..100, :id=>"slider2"%>
<%= f.range_field :income, :in=>0..100, :id=>"slider3"%>
<%= f.range_field :international, :in=>0..100,:id=>"slider4"%>
<%= f.submit "Save Profile" %>
<% end %>`
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] ="Welcome"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile Updated"
redirect_to #user
else render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :risk, :tax, :income, :international)
end
end
class User < ActiveRecord::Base
before_save :set_default
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with:
VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
def set_default
self.risk = "50"
self.tax = "50"
self.income = "50"
self.international = "50"
end
private
def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
end
end
And the output at the console, after I try to update params via range_field, and input User.find(4)
=> #<User id: 4, name: "Tony", email: "2#g.c", created_at: "2013-09-23 06:37:22",
updated_at: "2013-09-23 06:37:23", password_digest: "...", remember_token: "...",
risk: 50, tax: 50, income: 50, international: 50>
This is the output of my local WEBRick Rails Server
Started PATCH "/users/1" for 127.0.0.1 at 2013-09-23 06:15:32 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "user"=>{"risk"=>"70", "tax"=>"61", "income"=>"54", "international"=>"58"}, "commit"=>"Save Profile", "id"=>"1"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "1"]]
(0.0ms) begin transaction
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('2#g.c') AND "users"."id" != 1) LIMIT 1
(0.0ms) rollback transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '.....' LIMIT 1
(0.0ms) begin transaction
CACHE (0.0ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('2#g.c') AND "users"."id" != 1) LIMIT 1
(0.0ms) rollback transaction
Rendered users/edit.html.erb within layouts/application (1.9ms)
Rendered layouts/_header.html.erb (0.2ms)
Completed 200 OK in 8ms (Views: 6.0ms | ActiveRecord: 0.3ms)
Thank you all very much

The solution was simple
def update
#user = User.find(params[:id])
#user.update_attributes!(user_params)
.....

Related

Unpermitted parameters with simple_token_authentication

So I have a Participant model created in Rails API Mode.
This is what the controller of that looks like :
class ParticipantsController < ApplicationController
acts_as_token_authentication_handler_for User
def create
binding.pry
participant = Participant.new(participant_params)
puts "INFO: ----------------------------"
puts participant.inspect
puts params
if(participant.save)
render json: {
status: 'SUCCESS',
message: 'Participant link created',
data: participant
}, status: :created
else
render json: {
status: 'ERROR',
message: 'Participant link not created',
data: participant.errors
}, status: :unprocessable_entity
end
end
private def participant_params
params.permit(:id_request, :user_id)
end
end
Here is the User model :
class User < ApplicationRecord
acts_as_token_authenticatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates :firstname, presence: true
validates :lastname, presence: true
validates :username, presence: true
validates :address, presence: true
validates :idcard, presence: true
end
The second line you will see this : acts_as_token_authentication_handler_for User
This allows me to add authentication headers in my fetch request in React.
This is how I fetch it :
participateToContribution = id_request => {
const data = {
id_request: id_request,
user_id: localStorage.getItem('email')
}
console.log(data)
fetch('http://localhost:3000/participants', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-User-Email': localStorage.getItem('email'),
'X-User-Token': localStorage.getItem('token')
},
data: data
})
}
I've done this with other controllers and it works well, but now for some reasons when I fetch this rails returns me this error :
Started POST "/participants" for 127.0.0.1 at 2019-07-11 19:08:36 +0200
Processing by ParticipantsController#create as */*
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? ORDER BY "users"."id" ASC LIMIT ? [["email", "titivermeesch#gmail.com"], ["LIMIT", 1]]
↳ /home/tristan/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Unpermitted parameters: :user_email, :user_token
(0.2ms) begin transaction
↳ app/controllers/participants_controller.rb:7
(0.2ms) rollback transaction
↳ app/controllers/participants_controller.rb:7
Completed 422 Unprocessable Entity in 11ms (Views: 0.4ms | ActiveRecord: 0.7ms)
I tried to add those 2 fields in the .permit() but this gives me another error (I didn't have any of this in my other controllers, and it's just copy pasted).
Started POST "/participants" for 127.0.0.1 at 2019-07-11 19:13:15 +0200
(0.5ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
↳ /home/tristan/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Processing by ParticipantsController#create as */*
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? ORDER BY "users"."id" ASC LIMIT ? [["email", "titivermeesch#gmail.com"], ["LIMIT", 1]]
↳ /home/tristan/.rvm/gems/ruby-2.6.3/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Completed 500 Internal Server Error in 56ms (ActiveRecord: 3.3ms)
ActiveModel::UnknownAttributeError (unknown attribute 'user_email' for Participant.):
app/controllers/participants_controller.rb:5:in `create'
This is what is sent to this controller from the front-end part :
{id_request: 1, user_id: "titivermeesch#gmail.com"}
The GitHub code : https://github.com/titivermeesch/neighbourhood-app
pry output :
4: def create
=> 5: binding.pry
6: participant = Participant.new(participant_params)
7: puts "INFO: ----------------------------"
8: puts participant.inspect
9: puts params
10: if(participant.save)
11: render json: {
12: status: 'SUCCESS',
13: message: 'Participant link created',
14: data: participant
15: }, status: :created
16: else
17: render json: {
18: status: 'ERROR',
19: message: 'Participant link not created',
20: data: participant.errors
21: }, status: :unprocessable_entity
22: end
23: end
Update fetch with
fetch('http://localhost:3000/participants', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-User-Email': localStorage.getItem('email'),
'X-User-Token': localStorage.getItem('token')
},
body: JSON.stringify({data)
})
}
and data with
const data = {
participant: {
id_request: id_request,
user_id: localStorage.getItem('email')
}
}
and participant_params as
def participants_params
params.require(:participant).permit(:id_request, :user_id)
end
In this case, you should add those fields (email and token) to your User class. Here we have a complete example for https://gist.github.com/bosskovic/816623404b60b30726a8
Case you already have these fields, you just have to map user_email param to email column properly.
You are not permitting them here
private
def participants_params
params.permit(:id_request, :user_id)
end
You have to add them to that array like this
private
def participants_params
params.permit(:id_request, :user_id, :user_email, :user_token)
end
Now you said that that threw an error, because user_email is not a field on participant. You will need to add it in order to save a value to it, otherwise you need to take it out of the parameter array getting sent, and then the object will save. You can't send unpermitted paramters and try to save it, it just won't work.
The only other solution is to re write you create method, but this is not really a solution.
def create
participant = Participant.new
participant.request_id = params[:participant][:id_request]
participant.user_id = params[:participant][:user_id]
if(participant.save)
...

Error while setting up tagging for multiple models

I'm trying to setup multiple tagging in my app. I have used a single tag and tagging model which works fine for tagging a single model.
However, after setting up the association, i got this error when i wanted to create a new question undefined method map for nil:NilClass which actually points to <%= select_tag(:kategory_id, options_for_select(#kategories), :prompt => "Select Category", class: "form-control") %>. I don't know why it's pointing to this since it has nothing to do with the tagging. I might have made a mistake somewhere which i could not figure out.
Here is how i do the setup.
I added taggable_id and taggable_type to taggings table.
class AddAttributesToTaggings < ActiveRecord::Migration[5.1]
def change
add_column :taggings, :taggable_id, :integer
add_column :taggings, :taggable_type, :string
add_index :taggings, [:taggable_type, :taggable_id]
end
end
Tag.rb
class Tag < ApplicationRecord
has_many :taggings
has_many :posts, through: :taggings, source: :taggable, source_type: Post
has_many :questions, through: :taggings, source: :taggable, source_type: Question
has_many :user_tags, dependent: :destroy
has_many :users, through: :user_tags
extend FriendlyId
friendly_id :name, use: :slugged
def should_generate_new_friendly_id?
name_changed?
end
end
tagging.rb
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :taggable, :polymorphic => true
end
Question.rb
class Question < ApplicationRecord
belongs_to :user
belongs_to :kategory
validates :title, :content, presence: true
is_impressionable counter_cache: true, unique: :all
Question.order('impressions_count DESC')
scope :most_recent, -> { order(created_at: :desc) }
extend FriendlyId
friendly_id :title, use: :slugged
def should_generate_new_friendly_id?
title_changed?
end
include Taggable
def related_questions
Question.joins(:tags).where(tags: { id: self.tags.pluck(:id) }).where.not(id: self.id)
end
end
models/concerns/taggable.rb
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
def tag_list
self.tags.collect do |tag|
tag.name
end.join(", ")
end
def tag_list=(tags_string)
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
new_or_found_tags = tag_names.collect { |name| Tag.friendly.find_or_create_by(name: name) }
self.tags = new_or_found_tags
end
end
tags and taggings table in schema.rb
create_table "taggings", force: :cascade do |t|
t.bigint "tag_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "taggable_id"
t.string "taggable_type"
t.index ["tag_id"], name: "index_taggings_on_tag_id"
t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable_type_and_taggable_id"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.index ["slug"], name: "index_tags_on_slug", unique: true
end
questions/_form
<%= form_with(model: question, local: true) do |form| %>
<% if question.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<b><%= form.label "Category" %></b>
<%= select_tag(:kategory_id, options_for_select(#kategories), :prompt => "Select Category", class: "form-control") %>
</div>
<br>
<div class="field">
<b><%= form.label :title %></b>
<%= form.text_field :title, id: :question_title, class: "form-control" %>
</div>
<br>
<div class="field">
<%= form.label :content %>
<%= form.text_area :content, id: :question_content, class: "form-control" %>
</div>
<br>
<div>
<%= form.label :tag_list %>
<%= form.text_field :tag_list %>
</div>
<br>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
questions_controller.rb
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
before_action :find_contacts
before_action :find_kategory
impressionist :actions=>[:show,:index]
# GET /questions
# GET /questions.json
def index
if params[:kategory].blank?
#questions = Question.most_recent
else
#kategory_id = Kategory.find_by(name: params[:kategory]).id
#questions = Question.where(:kategory_id => #kategory_id).most_recent
end
end
# GET /questions/1
# GET /questions/1.json
def show
impressionist(#question)
#most_viewed = Question.order('impressions_count DESC').take(20)
end
# GET /questions/new
def new
#question = current_user.questions.build
#kategories = Kategory.all.map{ |k| [k.name, k.id] }
end
# GET /questions/1/edit
def edit
#kategories = Kategory.all.map{ |k| [k.name, k.id] }
end
# POST /questions
# POST /questions.json
def create
#question = current_user.questions.build(question_params)
#question.kategory_id = params[:kategory_id]
respond_to do |format|
if #question.save
flash[:success] = 'Your question was successfully posted.'
format.html { redirect_to #question }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
flash[:success] = 'Your question was successfully updated.'
format.html { redirect_to #question }
format.json { render :show, status: :ok, location: #question }
else
format.html { render :edit }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
flash[:success] = 'Your question has been deleted.'
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
def most_viewed
#questions = Question.order('impressions_count DESC').take(20)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.friendly.includes(:tags).find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :content, :kategory_id, :tag_list)
end
def find_contacts
#contacts = user_signed_in? ? current_user.all_active_contacts : ''
end
def find_kategory
#questions = Kategory.where(:kategory_id => #kategory_id)
end
end
UPDATE:**I'm now able to save a question without adding any tag but with tags added, I get a form error that tagging is invalid. **Tags are properly loaded when I edit a question.
Error Log:
Started POST "/questions" for 127.0.0.1 at 2018-07-13 12:01:01 +0100
Processing by QuestionsController#create as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"hgLn8bzd6fVSzCIPtRd3P0Jp/LEso
/h5cKNBY0a80x3i4RF6tpLlRyqmls3xRFryqyP1/s/rLDVIt9C1/uK+qw==", "kategory_id"=>"1"
, "question"=>{"title"=>"Help!!! My Dell Inspiron 6400 is hanging", "content"=>"
Lorem ipsum dolor amet seitan offal ethical, beard viral lo-fi put a bird on it
salvia actually yr. Ethical ennui pitchfork fanny pack, gentrify seitan sartoria
l bespoke. Viral 90's church-key swag, you probably haven't heard of them banh m
i intelligentsia brunch DIY iceland wolf pitchfork. Everyday carry photo booth n
ormcore XOXO tumblr portland.\r\n\r\nBrooklyn heirloom kombucha, edison bulb leg
gings hell of DIY chartreuse austin tacos bitters. Blog hexagon copper mug blue
bottle cray. Post-ironic direct trade kale chips mumblecore. Craft beer squid cr
onut vape, hoodie bitters succulents ramps snackwave vegan small batch brunch ch
ia food truck umami. Chillwave blue bottle viral raclette authentic health goth
vape coloring book cardigan 3 wolf moon taxidermy. Gluten-free tote bag hella, l
omo pinterest direct trade gastropub tattooed. Iceland hammock post-ironic, bush
wick cornhole tumeric dreamcatcher blog palo santo chartreuse 90's food truck sy
nth chicharrones.", "tag_list"=>"hardware"}, "commit"=>"Create Question"}
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDE
R BY "users"."id" ASC LIMIT $2 [["id", 3], ["LIMIT", 1]]
Private::Conversation Load (2.9ms) SELECT "private_conversations".* FROM "pri
vate_conversations" WHERE ("private_conversations"."recipient_id" = $1 OR "priva
te_conversations"."sender_id" = $2) [["recipient_id", 3], ["sender_id", 3]]
Private::Message Load (2.0ms) SELECT "private_messages".* FROM "private_messa
ges" WHERE "private_messages"."conversation_id" IN (2, 3, 4, 6)
Group::Conversation Load (3.9ms) SELECT "group_conversations".* FROM "group_c
onversations" INNER JOIN "group_conversations_users" ON "group_conversations"."i
d" = "group_conversations_users"."conversation_id" WHERE "group_conversations_us
ers"."user_id" = $1 [["user_id", 3]]
Group::Message Load (2.0ms) SELECT "group_messages".* FROM "group_messages" W
HERE "group_messages"."conversation_id" = 1
User Load (2.0ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (2, 3,
5)
(40.0ms) SELECT "group_conversations"."id" FROM "group_conversations" INNER
JOIN "group_conversations_users" ON "group_conversations"."id" = "group_conversa
tions_users"."conversation_id" WHERE "group_conversations_users"."user_id" = $1
[["user_id", 3]]
CACHE (0.0ms) SELECT "group_conversations"."id" FROM "group_conversations" I
NNER JOIN "group_conversations_users" ON "group_conversations"."id" = "group_con
versations_users"."conversation_id" WHERE "group_conversations_users"."user_id"
= $1 [["user_id", 3]]
User Load (2.9ms) SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."contact_id" WHERE "contacts"."user_id" = $1 AND "contacts
"."accepted" = $2 [["user_id", 3], ["accepted", "t"]]
User Load (4.9ms) SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."user_id" WHERE "contacts"."contact_id" = $1 AND "contacts
"."accepted" = $2 ORDER BY "users"."created_at" ASC [["contact_id", 3], ["accep
ted", "t"]]
Tag Load (2.0ms) SELECT "tags".* FROM "tags" WHERE "tags"."name" = $1 LIMIT
$2 [["name", "hardware"], ["LIMIT", 1]]
Kategory Load (10.7ms) SELECT "kategories".* FROM "kategories"
(1.0ms) BEGIN
Question Exists (4.9ms) SELECT 1 AS one FROM "questions" WHERE ("questions".
"id" IS NOT NULL) AND "questions"."slug" = $1 LIMIT $2 [["slug", "help-my-dell-
inspiron-6400-is-hanging"], ["LIMIT", 1]]
Kategory Load (5.9ms) SELECT "kategories".* FROM "kategories" WHERE "kategor
ies"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.0ms) ROLLBACK
Rendering questions/new.html.erb within layouts/application
Rendered questions/_form.html.erb (17.6ms)
Rendered questions/new.html.erb within layouts/application (141.6ms)
Rendered layouts/navigation/header/_toggle_button.html.erb (1.0ms)
Rendered layouts/navigation/header/_home_button.html.erb (30.3ms)
User Load (28.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIM
IT $2 [["id", 5], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
73.2ms)
User Load (2.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMI
T $2 [["id", 3], ["LIMIT", 1]]
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $
1 LIMIT $2 [["id", 5], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
109.4ms)
Rendered layouts/navigation/header/dropdowns/conversations/_group.html.erb (3.
9ms)
User Load (30.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIM
IT $2 [["id", 1], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
40.0ms)
CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $
1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
User Load (2.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMI
T $2 [["id", 2], ["LIMIT", 1]]
Rendered layouts/navigation/header/dropdowns/conversations/_private.html.erb (
100.6ms)
Rendered layouts/navigation/header/dropdowns/_conversations.html.erb (983.4ms)
User Load (2.0ms) SELECT "users".* FROM "users" INNER JOIN "contacts" ON "use
rs"."id" = "contacts"."user_id" WHERE "contacts"."contact_id" = $1 AND "contacts
"."accepted" = $2 [["contact_id", 3], ["accepted", "f"]]
Rendered layouts/navigation/header/dropdowns/contact_requests/_no_requests.htm
l.erb (1.0ms)
Rendered layouts/navigation/header/dropdowns/_contact_requests.html.erb (374.0
ms)
Rendered layouts/navigation/header/_dropdowns.html.erb (1713.9ms)
Rendered layouts/navigation/_header.html.erb (2485.4ms)
Kategory Load (2.9ms) SELECT "kategories".* FROM "kategories"
Rendered layouts/navigation/collapsible_elements/_constant_links.html.erb (2.0
ms)
(2.0ms) SELECT COUNT(*) FROM "questions" INNER JOIN "save_questions" ON "que
stions"."id" = "save_questions"."question_id" WHERE "save_questions"."user_id" =
$1 [["user_id", 3]]
Rendered layouts/navigation/collapsible_elements/_signed_in_links.html.erb (21
.5ms)
Rendered layouts/navigation/_collapsible_elements.html.erb (543.0ms)
Rendered layouts/_navigation.html.erb (3323.2ms)
Rendered layouts/application/_private_conversations_windows.html.erb (1.0ms)
Rendered layouts/application/_group_conversations_windows.html.erb (2.0ms)
Completed 200 OK in 22203ms (Views: 21796.6ms | ActiveRecord: 158.2ms)
You need to define #kategories = Kategory.all.map{ |k| [k.name, k.id] } in create and update actions. This actions rerender new/edit actions if they can't save the question, and they need #kategories for rerendering
If you provide the full server log for create action I can try to help you to figure out - why #question can not be saved
Looks to me like your #kategories, or something within there (perhaps a name), is nil, hence the error :) (Hopefully this is a good thing, as all may be well with your tagging.)
Ensure Kategory.all.map { |k| [k.name, k.id] } in your new action is returning records and this error should go away.
N.B. you can also use options_from_collection_for_select here.
# in the controller
def new
#question = current_user.questions.build
#kategories = Kategory.all
end
# and the view
<%= select_tag(:kategory_id, options_from_collection_for_select(#kategories, :name, :id), prompt: "Select Category", class: "form-control") %>
This way, if you don't have any categories, you won't get an error - instead, you just won't have any options available.

In Rails, my model association with a view is causing a new SELECT statement for every row

I have an issue with bringing a view into my app, which calculates a running balance for a transactions table using SQL. I have the following models:
account.rb
class Account < ApplicationRecord
belongs_to :user
has_many :transactions, dependent: :destroy
validates :name, presence: true, length: { maximum: 50, minimum: 2 }
validates :starting_balance, presence: true
#validates_associated :transactions
after_create :create_initial_transaction
def create_initial_transaction
self.update_attributes(current_balance: 0.00)
Transaction.create(trx_type: 'credit', trx_date: DateTime.now, account_id: self.id, description: "Starting Balance", amount: self.starting_balance)
#self.update_attributes(current_balance: #initbalance)
end
end
transaction.rb
class Transaction < ApplicationRecord
belongs_to :account
has_one :transaction_balance
delegate :running_balance, to: :transaction_balance
attr_accessor :trx_type
#default_scope { order('trx_date, id DESC') }
validates_presence_of :trx_type, :message => "Please select debit or credit"
validates :trx_date, presence: true
validates :description, presence: true, length: { maximum: 150 }
validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :memo, length: { maximum: 500 }
before_save :convert_amount
after_create :update_account_balance_new
after_update :update_account_balance_edit
after_destroy :update_account_balance_destroy
scope :with_balance, -> { joins(:transaction_balance) }
scope :desc, -> { order('trx_date, id DESC') }
# Determine the transaction_type for existing records based on amount
def transaction_type
if !new_record?
if self.amount >= 0
return ['Credit', 'credit']
else
return ['Debit', 'debit']
end
else
return ['Debit', 'debit']
end
end
private
def convert_amount
if self.trx_type == "debit"
self.amount = -self.amount.abs
end
end
def update_account_balance_new
#account = Account.find(account_id)
#account.update_attributes(current_balance: #account.current_balance + amount)
end
def update_account_balance_edit
#account = Account.find(account_id)
if saved_change_to_amount?
#account.update_attributes(current_balance: #account.current_balance - amount_was + amount)
end
end
def update_account_balance_destroy
#account = Account.find(account_id)
#account.update_attributes(current_balance: #account.current_balance - amount_was)
end
end
Basically my app allows users to create bank accounts, then add transactions to them to keep track of finances. I wanted to add a running balance at the transaction level, so I created a view which joins back to the transactions table 1:1 ....
Migration for view
class CreateTransactionBalancesView < ActiveRecord::Migration[5.1]
def up
execute <<-SQL
CREATE VIEW transaction_balances AS (
SELECT id AS transaction_id,
SUM(amount) OVER(PARTITION BY account_id ORDER BY trx_date, id) AS running_balance
FROM transactions
)
SQL
end
def down
execute("DROP VIEW transaction_balances")
end
end
Now, when I created a model for this view, I had issues referencing "belongs_to :transaction" because it was complaining that "transaction" was a reserved word, so I had to find a workaround, as seen in my model below:
transaction_balance.rb
class TransactionBalance < ApplicationRecord
self.primary_key = "transaction_id"
#belongs_to :transaction
belongs_to :user_transaction, foreign_key: "transaction_id", class_name: "Transaction"
end
transactions_controller.rb
class TransactionsController < ApplicationController
before_action :find_account
before_action :find_transaction, only: [:edit, :update, :show, :destroy]
# Index action to render all transactions
def index
#transactions = #account.transactions.paginate(page: params[:page], per_page: 25)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #transactions }
end
end
# New action for creating transaction
def new
#transaction = #account.transactions.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #transaction }
end
end
# Create action saves the trasaction into database
def create
#transaction = #account.transactions.build(transaction_params)
respond_to do |format|
if #transaction.save
format.html { redirect_to([#transaction.account, #transaction], :notice => 'Transaction was successfully created.') }
format.xml { render :xml => #transaction, :status => :created, :location => [#transaction.account, #transaction] }
else
format.html { render :action => "new" }
format.xml { render :xml => #transaction.errors, :status => :unprocessable_entity }
end
end
end
# Edit action retrieves the transaction and renders the edit page
def edit
end
# Update action updates the transaction with the new information
def update
respond_to do |format|
if #transaction.update_attributes(transaction_params)
format.html { redirect_to([#transaction.account, #transaction], :notice => 'Transaction was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #transaction.errors, :status => :unprocessable_entity }
end
end
end
# The show action renders the individual transaction after retrieving the the id
def show
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #transaction }
end
end
# The destroy action removes the transaction permanently from the database
def destroy
#transaction.destroy
respond_to do |format|
format.html { redirect_to(account_transactions_url) }
format.xml { head :ok }
end
end
private
def transaction_params
params.require(:transaction).permit(:trx_date, :description, :amount, :trx_type, :memo)
end
def find_account
#account = current_user.accounts.find(params[:account_id])
end
def find_transaction
#transaction = #account.transactions.find(params[:id])
end
end
And finally, my transactions index view, where I reference the running_balance field
<% #transactions.with_balance.desc.each do |transaction| %>
<tr class="row m-0">
<td class="col-sm-1 text-center"><%= link_to transaction.id, [transaction.account, transaction] %></td>
<td class="col-sm-1 text-center"><%= transaction.trx_date.strftime('%m/%d/%Y') %></td>
<td class="col-sm-4"><%= transaction.description %></td>
<td class="col-sm-2 text-right"><%= if transaction.amount >= 0 then number_to_currency(transaction.amount) end %></td>
<td class="col-sm-2 text-right"><%= if transaction.amount < 0 then "(" + number_to_currency(transaction.amount.abs) + ")" end %></td>
<td class="col-sm-2 text-right"><%= number_to_currency(transaction.running_balance) %></td>
</tr>
<% end %>
Now, my problem is when I access the transactions index page in browser, my server console shows the following:
Started GET "/accounts/1/transactions" for 127.0.0.1 at 2018-03-28 16:32:08 -0400
Processing by TransactionsController#index as HTML
Parameters: {"account_id"=>"1"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Account Load (1.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."user_id" = $1 AND "accounts"."id" = $2 LIMIT $3 [["user_id", 1], ["id", 1], ["LIMIT", 1]]
Rendering transactions/index.html.erb within layouts/application
Transaction Load (0.8ms) SELECT "transactions".* FROM "transactions" INNER JOIN "transaction_balances" ON "transaction_balances"."transaction_id" = "transactions"."id" WHERE "transactions"."account_id" = $1 ORDER BY trx_date, id DESC LIMIT $2 OFFSET $3 [["account_id", 1], ["LIMIT", 25], ["OFFSET", 0]]
TransactionBalance Load (0.3ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 8], ["LIMIT", 1]]
TransactionBalance Load (1.5ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 7], ["LIMIT", 1]]
TransactionBalance Load (0.3ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 6], ["LIMIT", 1]]
TransactionBalance Load (0.2ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 5], ["LIMIT", 1]]
TransactionBalance Load (0.2ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 4], ["LIMIT", 1]]
TransactionBalance Load (0.3ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 3], ["LIMIT", 1]]
TransactionBalance Load (1.0ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 1], ["LIMIT", 1]]
(1.8ms) SELECT COUNT(*) FROM "transactions" WHERE "transactions"."account_id" = $1 [["account_id", 1]]
Rendered transactions/index.html.erb within layouts/application (59.8ms)
Account Load (0.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."user_id" = $1 [["user_id", 1]]
Rendered layouts/_navbar.html.erb (3.9ms)
Completed 200 OK in 255ms (Views: 169.1ms | ActiveRecord: 19.5ms)
As seen above, the view is being selected from for each individual record. What I expect it to do is to join the transactions table with the transaction_balances view in one single select statement. Any help on this would be greatly appreciated! Thanks!
In your TransactionsController#index action, change this line:
#transactions = #account.transactions.paginate(page: params[:page], per_page: 25)
to this (credit to #engineersmnky):
#transactions = #account.transactions.includes(:transaction_balance).references(:transaction_balance).paginate(page: params[:page], per_page: 25)
This will generate a single query that allows your view to access the transaction_balance for each transaction without going back to the database.
This is happening because you're not loading the TransactionBalance records when you load the transactions in your controller. Here's what you're doing:
#account.transactions
And here's what will fix the problem:
#account.transactions.with_balance
This will use the with_balance scope in your model, which does joins(:balance), which will load both the transacations and all their balances in the one query.
I'm not sure where you're getting your #account from in your TransactionsController, but you may want to do a join or an includes.
For instance, #account = Account.includes(:transactions).find(params[:account_id]) before you query the transactions will eager load the transactions and pull them in one query rather than "n+1-ing" them.
The API dock entry can tell you more about the includes method
and
This article can tell you more about how to get rid of the n+1 querying problem.
Good luck!

File_size validation with file_validators gem unpermitted parameter

I added the file_validators gem to my app and called the validation in my vehicle_image.rb model that you can see below.
After attempting to upload a new image, I receive a unpermitted parameters message in the Rails console. I suspect the error has something to do with strong parameters? I attempted to assign the image prior to the if #vehicle.save but was unsuccessful.
edit:vehicle_image.rb
class VehicleImage < ActiveRecord::Base
belongs_to :vehicle
validates :image, file_size: { less_than_or_equal_to: 500.kilobytes, message: "Image must be less that 500kbs" }
mount_uploader :image, ImageUploader
def set_to_primary_and_save
VehicleImage.where(vehicle: vehicle).update_all(primary: false)
self.primary = true
save
end
end
stack trace
Started PATCH "/vehicles/65" for 127.0.0.1 at 2015-11-05 14:03:06 -0500
Processing by VehiclesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"o6W0JsKzxGe9D1z6VA2WeXW3b4JBVsfvYDvM4ANf4Eo5wVFBn1e31y+oKdLIsWFy41WXeW1BUenCzKTE6tni1Q==", "vehicle"=>{"make"=>"Pontiac", "model"=>"GTO", "year"=>"1967", "production_date"=>"January 5, 1968", "engine"=>"454 ", "transmission"=>"4 Speed Muncie", "trim"=>"Red", "color"=>"Black", "options"=>"Tinted Glass, Hurst Shifter", "location"=>"Milton, Ontario", "description"=>"sdfsdfdsf", "vehicle_images"=>{"image"=>[#<ActionDispatch::Http::UploadedFile:0x007f4adfa1c738 #tempfile=#<Tempfile:/tmp/RackMultipart20151105-7060-d0j694.jpg>, #original_filename="switzerland-3840x2160-alps-mountauns-stars-night-5713.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"vehicle[vehicle_images][image][]\"; filename=\"switzerland-3840x2160-alps-mountauns-stars-night-5713.jpg\"\r\nContent-Type: image/jpeg\r\n">], "image_cache"=>""}}, "commit"=>"Save", "id"=>"65"}
Vehicle Load (0.1ms) SELECT "vehicles".* FROM "vehicles" WHERE "vehicles"."id" = ? ORDER BY created_at DESC LIMIT 1 [["id", 65]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 6]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]]
Unpermitted parameter: vehicle_images
(0.0ms) begin transaction
(0.0ms) commit transaction
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "vehicle_images" ("image", "vehicle_id", "primary", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["image", "switzerland-3840x2160-alps-mountauns-stars-night-5713.jpg"], ["vehicle_id", 65], ["primary", "t"], ["created_at", "2015-11-05 19:03:06.685379"], ["updated_at", "2015-11-05 19:03:06.685379"]]
(18.5ms) commit transaction
Redirected to http://localhost:3000/vehicles/65
Completed 302 Found in 2474ms (ActiveRecord: 19.4ms)
vehicles_controller.rb
class VehiclesController < ApplicationController
def index
scope = Vehicle.approved
scope = scope.filter_by_make(params[:makes]) if params[:makes].present?
scope = scope.filter_by_year(params[:years]) if params[:years].present?
#vehicles = scope
authorize #vehicles
end
def show
#vehicle = Vehicle.find(params[:id])
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
end
def new
#vehicle = Vehicle.new
authorize #vehicle
end
def create
#vehicle = Vehicle.new(vehicle_params)
#vehicle.user = current_user
authorize #vehicle
if #vehicle.save
add_vehicle_images if params[:vehicle][:vehicle_images][:image]
create_registry_request(#vehicle)
flash[:notice] = "The Vehicle was sent to the Administrator for Approval. You will be notified in your Dashboard if your vehicle was approved or denied."
redirect_to current_user
else
flash[:error] = "There was an error saving the Vehicle to the Registry. Please try again."
render :new
end
end
def edit
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
end
def update
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
if #vehicle.update_attributes(vehicle_params)
add_vehicle_images if params[:vehicle][:vehicle_images][:image]
flash[:notice] = "The Vehicle entry was updated."
redirect_to #vehicle
else
flash[:error] = "There was an error updating the Vehicle. Please try again."
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
render :edit
end
end
def re_edit
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
end
def resubmit
#update and new request
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
if #vehicle.update_attributes(vehicle_params)
add_vehicle_images if params[:vehicle][:vehicle_images][:image]
Vehicle.transaction do
#vehicle.active_registry_request.archive
create_registry_request(#vehicle)
end
flash[:notice] = "The Vehicle entry was updated and sent to the Administrator. Please wait for Approval."
redirect_to #vehicle
else
flash[:error] = "There was an error updating the Vehicle. Please try again."
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
render :re_edit
end
end
private
def vehicle_params
params.require(:vehicle).permit(:make, :model, :year, :production_date, :engine, :transmission, :trim, :color, :options, :location, :description, vehicle_images_attributes: [:image])
end
def add_vehicle_images
params[:vehicle][:vehicle_images][:image].each_with_index do |img, i|
image = #vehicle.vehicle_images.build(image: img)
image.primary = true if i == 0
image.save!
end
end
def create_registry_request(vehicle)
RegistryRequest.create!(vehicle: vehicle)
end
end
Parameters: {
# ... snip ...
"vehicle" => { # ... snip ...
"vehicle_images"=>{ # ... snip ... }
}
}
But the parameter whitelist is specified as:
params.require(:vehicle).permit(..., vehicle_images_attributes: [:image])
"vehicle_images" is not equal to "vehicle_images_attributes", thus the message:
Unpermitted parameter: :vehicle_images
Either the form or the whitelist needs to change so that the key in the params hash matches the argument in permit.
Normally the _attributes suffix is added to the form when we use accepts_nested_attributes_for, but you don't appear to be doing that.

Token Fields - Record Inserted and then Deleted

I am trying to get token fields to work based on the RailsCasts episode #258 and I can submit the form just fine. When I go back to my main page, my tag does not appear.
Looking at the server logs on my local machine, I can see...
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'zviHK_WgeHLYUAtbKdzyfQ' LIMIT 1
Activity Load (0.1ms) SELECT "activities".* FROM "activities" WHERE "activities"."id" = ? LIMIT 1 [["id", "6"]]
(0.0ms) begin transaction
Tag Load (3.1ms) SELECT "tags".* FROM "tags" WHERE "tags"."id" = ? LIMIT 1 [["id", 1]]
Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "activities_tags" ON "tags"."id" = "activities_tags"."tag_id" WHERE "activities_tags"."activity_id" = 6
(0.3ms) INSERT INTO "activities_tags" ("activity_id", "tag_id") VALUES (6, 1)
(0.2ms) DELETE FROM "activities_tags" WHERE "activities_tags"."activity_id" = 6 AND "activities_tags"."tag_id" IN (1)
(1.6ms) commit transaction
The 'INSERT INTO "activities_tags"...' has the correct values but, for some reason, it is deleting the record immediately and I can't figure out where that would be coming into play.
My Activity controller:
class ActivitiesController < ApplicationController
...
def update
#activity = Activity.find(params[:id])
params[:activity][:tag_ids] ||= []
respond_to do |format|
if #activity.update_attributes(params[:activity])
flash[:success] = "Activity was successfully updated!"
format.html { redirect_to master_resumes_url }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
...
end
My Activity.rb:
class Activity < ActiveRecord::Base
attr_accessible :end_date, :organization, :position, :start_date, :user_id,
:tag_tokens, :tag_ids
attr_reader :tag_tokens
has_and_belongs_to_many :tags
belongs_to :user
def tag_tokens=(ids)
temp = ids.split(",")
self.tag_ids = temp[0]
end
end
My activity form:
<%= form_for(#activity) do |f| %>
<h4>Choose Tags</h4>
<%= f.label :tag_tokens, "Tags" %>
<%= f.text_field :tag_tokens, "data-pre" => #activity.tags.map(&:attributes).to_json %>
<p><%= link_to "Create A New Tag", new_tag_path %></p>
<% end %>
Finally, my activities.js:
$(function() {
$("#activity_tag_tokens").tokenInput("/tags.json", {
crossDomain: false,
prePopulate: $("#activity_tag_tokens").data("pre"),
theme: "facebook",
allowCustomEntry: true
});
});
Any thoughts?
1) Do you have a before_filter named anywhere?
2) Do you have :activities_tags named in attr_accessible?

Resources