Rails 4 Controller/Model associations and whitelisting attributes - ruby-on-rails

I have a Client model with the following line:
has_many :payments
and a Payments model with:
belongs_to :client
and
def generate_sepa
sdd = SEPA::DirectDebit.new(
name: 'name',
bic: 'bic',
iban: 'iban',
creditor_identifier: 'identifier'
)
SDD.add_transaction(
name: #client.company,
bic: #client.bic,
iban: #client.iban,
amount: self.amount,
reference: self.payment_reference,
remittance_information: self.remittance_information,
mandate_id: self.mandate_id,
mandate_date_of_signature: self.mandate_date_of_signature,
local_instrument: 'CORE',
sequence_type: self.sequence_type,
requested_date: self.date_of_payment,
batch_booking: self.batch_booking,)
sdd.to_xml
end
In payments show view I have
<%= #payment.generate_sepa %>
and in payments controller
def show
#client = Client.find(:id => params[:client_id])
end
Unfortunately I get the following error:
Unknown key: id
for
#client = Client.find(:id => params[:client_id])
In clients controller I also have:
def client_params
params.require(:client).permit(:id, :trading_name, :company_name, :owner, :main_contact_name,
:email, :phone, :date_joined, :trading_street_one, :trading_street_two, :trading_town, :trading_county, :iban, :bic)
end
and in payments:
def payment_params
params.require(:payment).permit(:client_id, :signup_fee, :monthly_fee, :date_of_payment, :payment_reference,
:remittance_information, :mandate_id, :mandate_date_of_signature, :batch_booking, :sequence_type, :is_recurring, :is_onceoff)
end
Is there an issue with the way I've whitelisted id? Or is there something wrong with the association between client and payments because, to be honest, I'm having a tough time figuring out what's going wrong.
EDIT
:client_id gets passed to payments like so when I create a new client:
def create
#client = Client.new(client_params)
respond_to do |format|
if #client.save
format.html { redirect_to new_payment_url(:client_id => #client.id) }
format.json { render action: 'show', status: :created, location: #client }
else
format.html { render action: 'new' }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
in the payments _form partial I also have:
<%= f.hidden_field('client_id', :value => params[:client_id]) %>
along with the rest of the form fields.

In payment controller/show #client = Client.find(params[:id]) will not work.
Use
#payment= Payment.find(params[:id])
#client = #payment.client
Because you are passing payment id to the show, not the client id.

def show
#client = Client.find(params[:client_id])
end
Isn't it?

Related

Rails 5 Nested attributes "Unpermitted parameter" - Whitelisted

Error: Unpermitted parameter: properties
I'm whitelisting the properties{} in the request_controller.rb
This usually works but not this time.
I'm not been able to save some of the data entered in a form. The 3 fields that are not saving are coming from a dynamic form "request_type". I followed Rails Cast episode 403 for this solution, which I have working well in another project but not in this one.
Source: http://railscasts.com/episodes/403-dynamic-forms
Sorry if this is a duplicate question, but I've looked at several other questions and I can't pin-point what I'm doing wrong here
I've researched several questions here, but I'm still not able to get it to work:
Rails 4 Nested Attributes Unpermitted Parameters
Nested attributes - Unpermitted parameters Rails 4
I'm omitting some stuff to make it easier to read the code. Please ask me if you need to see more.
Here's the log:
Processing by RequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8EASewOIxY58b+SU+dxd2YAfpjt38IdwNSju69RPwl/OKfx3AfmvLav79igj8CqPbDwi0eJAwojRbtm+C9F6wg==", "request"=>{"name"=>"asdasddaa", "due_date(1i)"=>"2016", "due_date(2i)"=>"9", "due_date(3i)"=>"15", "user_id"=>"1", "project_id"=>"1", "request_type_id"=>"2", "properties"=>{"Name and last name"=>"asdasd", "Mobile"=>"asdada", "Office tel."=>"asdadas"}}, "commit"=>"Create Request"}
Unpermitted parameter: properties
Update
If I change the request_params to this:
def request_params
params.require(:request).permit(:name, :due_date, :group_id, :user_id, :project_id, :request_type_id, properties:{} ).tap do |whitelisted|
whitelisted[:properties] = params[:request][:properties]
end
end
See:
properties:{}
I get this Error:
Unpermitted parameters: Name and last name, Mobile, Office tel.
request_controller.rb
def new
#request = Request.new
#request_type = RequestType.find(params[:request_type_id])
#project = #request_type.project.id
end
def create
#request = Request.new(request_params)
respond_to do |format|
if #request.save
format.html { redirect_to #request, notice: 'Request was successfully created.' }
format.json { render :show, status: :created, location: #request }
else
format.html { render :new }
format.json { render json: #request.errors, status: :unprocessable_entity }
end
end
end
def request_params
params.require(:request).permit(:name, :due_date, :group_id, :user_id, :project_id, :request_type_id, :properties).tap do |whitelisted|
whitelisted[:properties] = params[:request][:properties]
end
end
request_types_controller.rb
def new
#request_type = RequestType.new
#project = Project.find(params[:project])
end
def create
#request_type = RequestType.new(request_type_params)
respond_to do |format|
if #request_type.save
format.html { redirect_to #request_type, notice: 'Request type was successfully created.' }
format.json { render :show, status: :created, location: #request_type }
else
format.html { render :new }
format.json { render json: #request_type.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #request_type.update(request_type_params)
format.html { redirect_to #request_type, notice: 'Request type was successfully updated.' }
format.json { render :show, status: :ok, location: #request_type }
else
format.html { render :edit }
format.json { render json: #request_type.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_request_type
#request_type = RequestType.find(params[:id])
end
def request_type_params
params.require(:request_type).permit(:name, :project_id, properties:{}, fields_attributes: [:name, :field_type, :required, :id, :_destroy])
# params.require(:product_type).permit(:name, :product_type_id)
end
models/request.rb
class Request < ApplicationRecord
belongs_to :group
belongs_to :user
belongs_to :project
belongs_to :request_type
serialize :properties, Hash
end
models/request_type.rb
class RequestType < ApplicationRecord
belongs_to :project
has_many :fields, class_name: "RequestField"
accepts_nested_attributes_for :fields, allow_destroy: true
has_many :requests
end
models/request_field.rb
class RequestField < ApplicationRecord
belongs_to :request_type
end
views/requests/new.html.erb
<%= form_for #request do |f| %>
<%= f.fields_for :properties, OpenStruct.new(#request.properties) do |builder| %>
<% #request_type.fields.each do |field| %>
<%= render "requests/fields/#{field.field_type}", field: field, f: builder %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit class:"btn btn-primary" %>
</div>
<% end %>
Try removing :properties from your request_params in your request_controller like this:
def request_params
params.require(:request).permit(:name, :due_date, :group_id, :user_id, :project_id, :request_type_id).tap do |whitelisted|
whitelisted[:properties] = params[:request][:properties]
end
EDIT
def request_params
params.require(:request).permit(:id, :name, :due_date, :group_id, :user_id, :project_id, :request_type_id)
params.require(:properties).permit!
end!

Ruby on Rails: How to display previous values in select_tag

this is my first question on stackoverflow and I'm new to Ruby on Rails, so I'm really sorry if I'm asking the question wrong.
My problem: I have two classes (word and category) and now I try to assign several categories to one word over the variable syntactical_category, for that I use a select_tag. This works nicely, when the user creates a new word as you can see in this snippet from the command line:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"EwGk/QrOma4JGJhSBjAT6fW9BqvuCJCEPYgNC9i/okOuQZBbh1ArNEfvuHvDRwY0Q2tABYTc/b3n3tAIlQmJRg==",
"word"=>{
"name_de"=>"This is a test",
"description_de"=>"Test Description",
"syntactical_category"=>"5721e7fa8cc4b3285c000004",
"semantical_categories_ids"=>["57921a358cc4b36e42000001"]
},
"locale"=>"de"
}
But when the user tries to edit a word afterwards, the select_tag ignores the value(s) from the database and only displays the first category of all potential categories and overwrites the old value(s) if the user submits the word again. This is not what i want.
Does anybody have a solution for this problem?
I'm using Ruby 2.2.3, RubyonRails 4.2.4 and MongoDB 3.0.9, Mongoid and haml if this information helps you.
Please find below my code.
word.rb
class Word
include Mongoid::Document
include Mongoid::Attributes::Dynamic
include MultiLanguageText
belongs_to :syntactical_category, class_name: "Category", inverse_of: nil, :autosave => true
has_and_belongs_to_many :semantical_categories, class_name: "Category", inverse_of: nil, :autosave => true
field :name_de, type: String
field :name_en, type: String
field :description_de, type: String
field :description_en, type: String
end
words_controller.rb
class WordsController < ApplicationController
helper MultiLanguageText
def index
#word = Word.all
end
def show
#word = Word.find(params[:id])
end
def new
#word = Word.new
end
def create
#word = Word.new(word_params)
respond_to do |format|
if #word.save
format.html { redirect_to #word, notice: t("word_create_success") }
format.json { render :index, status: :created, location: #word }
else
format.html { render :new }
format.json { render json: #word.errors, status: :unprocessable_entity }
end
end
end
def edit
#word = Word.find(params[:id])
end
def update
#word = Word.find params[:id]
#word.update_attributes(word_params)
respond_to do |format|
if #word.update(word_params)
format.html { redirect_to #word, notice: t("word_update_success") }
format.json { render :index, status: :ok, location: #word }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#word = Word.find(params[:id])
#word.destroy
flash[:notice] = t(:delete_confirmation)
redirect_to words_path
end
def word_params
allow = [:name_de, :name_en, :description_de, :description_en, :syntactical_category, {:semantical_categories_ids => []}]
params.require(:word).permit(allow)
end
end
words/_form.html.haml
= form_for #word do |w|
.field
.input-append
= select_tag 'word[semantical_categories_ids][]', options_for_select(Category.where(category_type: 'semantic').where(:name_de.exists => true).collect{|c| [c.name_de, c.id]})
Thank you in advance for your help!
Just pass #word.semantical_categories_ids after the collection
= select_tag 'word[semantical_categories_ids][]', options_for_select(Category.where(category_type: 'semantic').where(:name_de.exists => true).collect{|c| [c.name_de, c.id]}, #word.semantical_categories_ids)
and I think it should accept multiple values. If yes add multiple: true at the end
I think you have multiple selection select_tag
So, you have to do below
= select_tag 'word[semantical_categories_ids][]', options_for_select(YOUR_COLLECTION,ARRAY_OF_SELECTED_VALUES),{multiple: true}
Note: Add ,{multiple: true} to your select_tag
i found a solution for my problem. I'm not sure if this is "the way to do it" but for me it works fine.
- #word['semantical_categories_ids'] = [''] if !#word['semantical_categories_ids'].is_a? Array
- #word['semantical_categories_ids'].each do |entry|
= select_tag 'word[semantical_categories_ids][]', options_for_select(Category.where(category_type: 'semantic').where(:name_de.exists => true).collect{|c| [c.name_de, c.id]}, entry)

Rails 4 is not saving into the database with this method?

Trying to save parameters from url into a database.
I have a link:
- #kits.each do |kit|
= link_to 'Submit Video', new_challenge_path(kit: kit)
#this will append a new parameter into the url
The link goes to a form page with this:
= simple_form_for #challenge, html: { class: "form-horizontal" } do |f|
= f.input :video_title
= f.input :video_url
= f.input :video_desc, as: :text
= f.button :submit, "Upload video"
In my controller, I have this:
def create
#challenge = Challenge.new(challenge_params)
#challenge.kit_id = params[:kit]
respond_to do |format|
if #challenge.save
format.html { redirect_to #challenge, notice: 'Challenge was successfully created.' }
format.json { render :show, status: :created, location: #challenge }
else
format.html { render :new }
format.json { render json: #challenge.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_challenge
#challenge = Challenge.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def challenge_params
params.require(:challenge).permit(:video_title, :video_url, :video_desc, :kit_id)
end
Association between kit and challenge
class Challenge < ActiveRecord::Base
belongs_to :kit, counter_cache: true
end
class Kit < ActiveRecord::Base
has_many :challenges
end
The parameter doesn't save into the :kit_id. Doesn't this: #challenge.kit_id = params[:kit] supposed to take care of saving it?
You are right to do the kit_id assignment in controller, not in the form, since doing so in the form (even by means of using the hidden field) is not secure, because changing the value of a hidden_field is just a matter of inspecting the elements of the page.
What you are doing wrong, is I assume that params[:kit] is just nil.
You would probably want to use params[:kit_id].
If that won't help, put binding.pry here:
# ... code omitted
#challenge.kit_id = params[:kit]
binding.pry
# ... code omitted
and check the value of params in opened console.

Rails 4 : find or create method

I have an association between experience and company where company has_many :experiences and experience belongs_to :company, Now I want to design this: if a user enters a company name that already exist in the company model it will assign to it but if the user put a company name that doesn't already exist in company model - the company must persist but not be created as a new record (I don't want to use find_or_create method and the find_by doesn't seem working for this situation)
Edit
this is my Company controller
def allcompanies
#companies = Company.paginate(page: params[:page], per_page: 10).order("created_at DESC")
end
def show
#company = Company.find_by_slug(params[:id])
if #company
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
def index
#companies = Company.limit(19).order("id DESC")
end
def new
#company = Company.new
end
def edit
end
def create
#company = Company.new(company_params)
respond_to do |format|
if #company.save
flash[:notice] = "Entreprise '#{#company.name}' Crée!"
format.html { redirect_to #company}
else
format.html { render action: 'new' }
end
end
end
def update
respond_to do |format|
if #company.update(company_params)
flash[:notice] = "Entreprise '#{#company.name}' à été mis à jour"
format.html { redirect_to #company }
else
format.html { render action: 'edit' }
end
end
end
def destroy
#company.destroy
respond_to do |format|
format.html { redirect_to companies_url }
end
end
private
def set_company
#company = Company.find_by_slug(params[:id])
end
def company_params
params.require(:company).permit(:name, :company_description, :country, :city, :company_status, :company_size, :company_website, :sector_ids, :job_id, :companylogo)
end
and this is my experience model that actually create a new record for each company
belongs_to :profile
belongs_to :company
validates :company_name, presence: true
def company_name
company.try(:name)
end
def company_name=(name)
self.company = Company.find_or_initialize_by(name: name)
end
OK, so you want some companies to be visible and some not.
Then, you can create a corresponding boolean field, like public_visible and set it to false for companies you don't know(ie created by application users).
In your controller do something like this:
#company = Company.find_by_name(params[:company][:name])
if #company.blank?
#company = Company.create(params[:company].merge({ public_visible: false }))
end
#company.experiences.create(...)
it is just an example, since I don't know your real conditions on companies creation, so you need to adjust it according to your needs.

Rails: model accepts nested attributes but controller doesn't seem to care

I'm struggling to get nested attributes down. Working off of Railscast 196, I tried to setup my own app that does basic nesting. Users can create scavenger hunts. Each hunt consists of a series of tasks (that can belong to any hunt, not just one). I got a little help here and tried to learn from a post with a similar issue, but I'm still stuck. I've been hacking around for hours and I've hit a brick wall.
class HuntsController < ApplicationController
def index
#title = "All Hunts"
#hunts = Hunt.paginate(:page => params[:page])
end
def show
#hunt = Hunt.find(params[:id])
#title = #hunt.name
#tasks = #hunst.tasks.paginate(:page => params[:page])
end
def new
if current_user?(nil) then
redirect_to signin_path
else
#hunt = Hunt.new
#title = "New Hunt"
3.times do
#hunt = #hunt.tasks.build
#hunt = #hunt.hunt_tasks.build
hunt = #hunt.hunt_tasks.build.build_task
end
end
end
def create
#hunt = Hunt.new(params[:hunt])
if #hunt.save
flash[:success] = "Hunt created!"
redirect_to hunts_path
else
#title = "New Hunt"
render 'new'
end
end
....
end
With this code, when I try and create a new hunt, I'm told that there's no method "build_task" (it's undefined). So when I remove that line and use the second bit of code that's commented out above, I get the error below.
NoMethodError in Hunts#new
Showing /Users/bendowney/Sites/MyChi/app/views/shared/_error_messages.html.erb where line #1 raised:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.errors
Extracted source (around line #1):
1: <% if object.errors.any? %>
2: <div id="error_explanation">
3: <h2><%= pluralize(object.errors.count, "error") %>
4: prohibited this <%= object.class.to_s.underscore.humanize.downcase %>
Trace of template inclusion: app/views/tasks/_fields.html.erb, app/views/hunts/_fields.html.erb, app/views/hunts/new.html.erb
And when I use the first bit of code that's commented out in the hunt controller, then I get an error telling me that my 'new' method has an unintialized constant:
NameError in HuntsController#new
uninitialized constant Hunt::Tasks
I'm at my wit's end. Any suggestions on what exactly I'm doing wrong? Or a strategy Here are my models:
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
has_many :tasks, :through => :hunt_tasks #, :foreign_key => :hunt_id
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class Task < ActiveRecord::Base
has_many :hunt_tasks
has_many :hunts, :through => :hunt_tasks#, :foreign_key => :hunt_id
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class HuntTask < ActiveRecord::Base
belongs_to :hunts # the id for the association is in this table
belongs_to :tasks
end
When you create an association between 2 of your models, you add functionality to them, depending on how you define your relationship. Each type kinda adds different functions to your model.
I really recommend reading this guide -> http://guides.rubyonrails.org/association_basics.html
Here you can see which functions get added by each different type of association.
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
If I do a small sample program like...
class HuntsController < ApplicationController
# GET /hunts
# GET /hunts.json
def index
#hunts = Hunt.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #hunts }
end
end
# GET /hunts/1
# GET /hunts/1.json
def show
#hunt = Hunt.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #hunt }
end
end
# GET /hunts/new
# GET /hunts/new.json
def new
#hunt = Hunt.new
3.times do |i|
t = #hunt.hunt_tasks.build
t.name = "task-#{i}"
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #hunt }
end
end
# GET /hunts/1/edit
def edit
#hunt = Hunt.find(params[:id])
end
# POST /hunts
# POST /hunts.json
def create
#hunt = Hunt.new(params[:hunt])
respond_to do |format|
if #hunt.save
format.html { redirect_to #hunt, notice: 'Hunt was successfully created.' }
format.json { render json: #hunt, status: :created, location: #hunt }
else
format.html { render action: "new" }
format.json { render json: #hunt.errors, status: :unprocessable_entity }
end
end
end
# PUT /hunts/1
# PUT /hunts/1.json
def update
#hunt = Hunt.find(params[:id])
respond_to do |format|
if #hunt.update_attributes(params[:hunt])
format.html { redirect_to #hunt, notice: 'Hunt was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #hunt.errors, status: :unprocessable_entity }
end
end
end
# DELETE /hunts/1
# DELETE /hunts/1.json
def destroy
#hunt = Hunt.find(params[:id])
#hunt.destroy
respond_to do |format|
format.html { redirect_to hunts_url }
format.json { head :no_content }
end
end
end
and this model-relation
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
end
class HuntTask < ActiveRecord::Base
belongs_to :hunt
end
and add this snippet somewhere in views/hunts/_form.html
<% #hunt.hunt_tasks.each do |t| %>
<li><%= t.name %></li>
<% end %>
I get regular output, seeing that the 3 tasks were created.
have you tried
hunttask = #hunt.build_hunt_task
in the HuntsController new action?
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
The immediate error you are seeing is in app/views/shared/_error_messages.html.erb. object is not defined, You probably need to find where that partial is called. Find:
render :partial=>"/shared/error"
replace it with
render :partial=>"/shared/error", :locals=>{:object=>#hunt}
If you find it in app/views/hunts somewhere, if you find in in app/views/tasks, replace #hunt with #task
That will at least show you what the real error is.

Resources