Ruby on rails: Updating attribute on an association - ruby-on-rails

In my controller I have the following:
def sort
params[:order].each do |key,value|
Question.find(value[:id]).update_attribute(:order,value[:order])
end
render :nothing => true
end
This works perfectly to update the order column for the 'Question' item.
However i've now moved the order column to a new table 'question_sections' which is associated to Questions.
class Question < ActiveRecord::Base
has_many :sections, :through => :question_sections
belongs_to :section
has_many :question_sections
default_scope { order(order: :asc) }
accepts_nested_attributes_for :section, :reject_if => :all_blank, allow_destroy: true
accepts_nested_attributes_for :question_sections, :reject_if => :all_blank, allow_destroy: true
end
I'm trying to adapt the sort function to update the 'order' column in 'question_sections' but am having trouble with it.
Any help on what the function should look like?

In case you are using nested attributes, you shoud call the includes method, and then iterate over each question_sections:
def sort
params[:order].each do |key,value|
questions = Question.includes(:question_sections).find(value[:id])
questions.question_sections.each { |q| q.update_attribute(:order,value[:order]) }
end
render :nothing => true
end
This breaks the problems into 2 parts, load all the question_sections needed:
1) Load all the question_sections of a question:
questions = Question.includes(:question_sections).find(value[:id])
Question Load
SELECT "questions".* FROM "questions" WHERE "questions"."id" = ? LIMIT 1 [["id", 1]]
QuestionSections Load
SELECT "question_sections".* FROM "question_sections" WHERE "question_sections"."question_id" IN (1)
2) update this question_sections
questions.question_sections.each { |q| q.update_attribute(:order,value[:order]) }
QuestionSections Update
UPDATE "question_sections" SET "order" = ?, "updated_at" = ? WHERE "question_sections"."id" = ? [["order", "different order now"], ["updated_at", "2017-03-09 13:24:42.452593"], ["id", 1]]

I think if you are using nested_attributes for Question model here then Rails should automatically update the nested params for QuestionSection
The controller should look something like this:
def sort
#question = Question.find_by(id: params[:id])
#question.update_attributes(question_params)
end
private
def question_params
params.require(:question).permit!
end
The parameters received to the controller should be like :
params = { question: {
abc: 'abc', question_sections_attributes: [
{ order_id: 1, ... },
{ order_id: 2, ... },
{ order_id: 3, ... }
]
}}
I hope this helps :)

Related

Scoping in Rails with a has_one relationship and an OR

I'm having trouble setting up a scope that will check a value on a has_one relationship. I have a Document model. Each Document has_one Document::Response, and Document::Response has a status field (I generated the Document::Response model with rails g model documents/response, which made things a bit more confusing than I'd anticipated. The strong parameters method references it as :document_responses for example. Not sure if it's relevant though. )
scope :rejected, -> { joins(:response).where(status: 'rejected') }
I'll also need to check for something else once I get that working:
scope :rejected, -> { joins(:response).where(status: 'rejected').or(status: 'owed' }
But I can't seem to get the syntax right on this.
PG::UndefinedColumn: ERROR: column documents.status does not exist
LINE 1: ...ts"."id" WHERE "documents"."mortgage_id" = $1 AND "documents...
^
: SELECT COUNT(*) FROM "documents" INNER JOIN "document_responses" ON
"document_responses"."document_id" = "documents"."id" WHERE
"documents"."mortgage_id" = $1 AND "documents"."status" = $2
Routes:
resources :mortgages, shallow: true do
resources :documents do
collection do
post :create_templates
end
end
member do
post :archive
end
end
resources :responses, controller: 'document/responses' do
member do
post :accept
post :reject
end
end
It is required to explicitly tell AR to use status column from responses table instead of documents:
scope :rejected, -> {
joins(:response).where(responses: { status: 'rejected' })
}
scope :processed, -> {
joins(:response).where(responses: { status: ['rejected', 'owed'] })
}

Update has_many attributes with irregular params

I have model LoanPlan and Career, they are associated by a join_table
The request params from another frontend developer will be like this
"loan_plan" => {
"id" => 32,
"careers" => [
[0] {
"id" => 8,
},
[1] {
"id" => 9,
}
]
},
However, I got ActiveRecord::AssociationTypeMismatch: Career(#70198754219580) expected, got ActionController::Parameters(#70198701106200) in the update method
def update
#loan_plan.update(loan_plan_params)
end
When I tried to update the loan_plan model with careers params, it expects the params["careers"] should be careers object of a array instead of ids of a array.
So my workround is to manually fectch the careers objects of a array and replace the sanitized params.
It seems dirty and smells bad, any better solution in Rails way? Thanks
def loan_plan_params
# params.fetch(:loan_plan, {})
cleaned_params = params.require(:loan_plan).permit(
:id,
:name,
{:careers=>:id}
)
cleaned_params["careers"] = Career.find(cleaned_params["careers"].map{|t| t["id"]})
cleaned_params
end
model
class LoanPlan < ActiveRecord::Base
has_and_belongs_to_many :careers
accepts_nested_attributes_for :careers
end
In Rails way, params should be
"loan_plan" => {
"id" => "32",
"career_ids" => ["8", "9"]
}
and the strong parameter loan_plan_params should be
def loan_plan_params
params.require(:loan_plan).permit(
:id,
:name,
:career_ids => []
)
end

Inner json objects not rendering Rails

First model:
class AdPlace < ActiveRecord::Base
belongs_to :user
has_many :user_ads, dependent: :destroy
accepts_nested_attributes_for :user_ads, allow_destroy: true, reject_if: :all_blank
def ad_places_json
self.as_json(
include: {
user_ads: {
}
})
end
end
Second Model:
class UserAd < ActiveRecord::Base
belongs_to :ad_place
end
From my controller I am trying to get all AdPlaces having all UserAds.
So my method of controller is following:
def get_ads
ad_places = AdPlace.includes(:user_ads).where(user_id: #current_user.id)
render json: {
ad_places: ad_places.each {|ad| ad.ad_places_json},
message: ['success']
},
status: :ok
end
The above function renders all AdPlaces but UserAds.
The SELECT query on server is the following:
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 7]]
AdPlace Load (0.9ms) SELECT "ad_places".* FROM "ad_places"
UserAd Load (0.5ms) SELECT "user_ads".* FROM "user_ads" WHERE "user_ads"."ad_place_id" IN (1, 8, 9, 10, 11, 12, 13)
Everything seems perfect to me, I am confused where am I making mistake.
Thank you.
Change each to map:
ad_places.map {|ad| ad.ad_places_json}
Array#each vs. Array#map

Why is this reject_if in my model not rejecting blank records?

In the following class...
class Parent < ActiveRecord::Base
has_many :relatives
has_many :kids, through: :relatives
accepts_nested_attributes_for :relatives,
:reject_if => lambda { |a| a['kid_id'].blank? },
:allow_destroy => true
end
...I've added some code which I expected to prevent 'relatives' from saving on the parent model if their fields were blank. It doesn't seem to work. It saved both blank and non-blank fields.
I.e. when I go into the console and query the database, I can pull out the parent record and run queries like so:
2.2.2 :004 > p.relatives.find(17)
Relative Load (5.4ms) SELECT "relatives".* FROM "relatives" WHERE "relatives"."parent_id" = ? AND "relatives"."id" = ? LIMIT 1 [["parent_id", 24], ["id", 17]]
=> #<Relative id: 17, relationship: "yes", parent_id: 24, kid_id: 1, created_at: "2015-11-12 09:56:07", updated_at: "2015-11-12 09:56:07">
That's what I expected - I entered data for that 'kid'.
2.2.2 :005 > r = p.relatives.find(18)
Relative Load (3.4ms) SELECT "relatives".* FROM "relatives" WHERE "relatives"."parent_id" = ? AND "relatives"."id" = ? LIMIT 1 [["parent_id", 24], ["id", 18]]
=> #<Relative id: 18, relationship: "", parent_id: 24, kid_id: nil, created_at: "2015-11-12 09:56:07", updated_at: "2015-11-12 09:56:07">
This record should never have saved because it violates the lambda above, i.e. ...
2.2.2 :006 > r.relationship.blank?
=> true
2.2.2 :007 > r.kid.blank?
=> true
...the fields are blank!
Here's the controller in question (extract):
class ParentsController < ApplicationController
before_action :set_parent, only: [:show, :edit, :update, :destroy]
before_action :lookup_kids, only: [:new, :edit]
# GET /parents/new
def new
#parent = Parent.new
4.times { #parent.relatives.build }
end
# POST /parents
# POST /parents.json
def create
logger.debug(" SANITY CHECK ")
logger.debug(parent_params)
#parent = Parent.new(parent_params)
parent_params[:relatives_attributes].each do |k,r|
#parent.relatives.build(r.except(:_destroy))
end
respond_to do |format|
if #parent.save
format.html { redirect_to #parent, notice: 'Parent was successfully created.' }
format.json { render :show, status: :created, location: #parent }
else
format.html { render :new }
format.json { render json: #parent.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_parent
#parent = Parent.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def parent_params
params.require(:parent).permit(:name,
relatives_attributes: [:parent_id, :kid_id, :relationship, :_destroy])
end
def lookup_kids
#kids = Kid.all
end
end
I think this might help
https://fabianosoriani.wordpress.com/2011/06/07/accepts_nested_attributes_for-3-0-5-reject_if-still-have-gotchas/
There are some gotchas behind accepts_nested_attributes_for with validations.
Based on my understanding since model kid is associated by Relative, you should do like this. Try this in your Parent Model
accepts_nested_attributes_for :relatives, :allow_destroy => true
Then in Your Relative Model,
accepts_nested_attributes_for :kids,
:reject_if => lambda { |a| a['kid_id'].blank? },
:allow_destroy => true
Thanks to #user5554692, discovered the only way to do this is as follows:
Adding this to the parent controller:
parent_params[:relatives_attributes].delete_if { |k,v| v['kid_id'].blank?}
Removed all blank records, then...
#parent = Parent.new(parent_params)
Creates the parent object as usual.
Apparently, reject_if just doesn't work in this particular scenario, for some reason.

Conditional issue using relationship

I'm trying to search a column from another table using three tables in relationship.
In my view policy_vehicles I have a text_field_tag and want to search by "raz_soc".
My tables are:
Policy_vehicles
|id| |policy_id|
integer integer
100 1
200 2
Policies
|id| |client_id|
integer integer
1 1
2 2
Clients
|id| |raz_soc|
integer varchar(255)
1 MARATEX SAC
2 ATT
This is my controller:
class PolicyManagement::PolicyController < ApplicationController
def generate_print_per_vehicle
params[:search_policy_id] = Policy.find(:all,:joins => :client ,:conditions => ['raz_soc LIKE ?',"%#{params[:search_raz_soc]}%" ])
#policies= PolicyVehicle.find(:all,:joins => :policy, :conditions => ['policy_id = ?',params[:search_policy_id] ])
end
end
This is my model:
class Policy < ActiveRecord::Base
belongs_to :client
has_many :policy_vehicles
end
class PolicyVehicle < ActiveRecord::Base
belongs_to :policy
end
class Client < ActiveRecord::Base
has_many :policies
end
This is my view where I'm trying to find by a column from another table:
<% form_tag :controller=>"policy_management/policy",:action=>"generate_print_per_vehicle" do %>
Society:
<%= text_field_tag "search_raz_soc",params[:search_raz_soc] %>
<%= submit_tag "Buscar", :name => nil %>
<% end %>
My logs are:
Mysql::Error: Operand should contain 1 column(s): SELECT `policy_vehicles`.* FROM `policy_vehicles` INNER JOIN `policies` ON `policies`.id = `policy_vehicles`.policy_id WHERE (policy_id = 166,1540,2822,4074)
It should be a search like this:
Policy Load (3.1ms) SELECT `policies`.* FROM `policies` INNER JOIN `clients` ON `clients`.id = `policies`.client_id WHERE (raz_soc = 'MARATEX SAC')
PolicyVehicle Load (0.3ms) SELECT `policy_vehicles`.* FROM `policy_vehicles` INNER JOIN `policies` ON `policies`.id = `policy_vehicles`.policy_id WHERE (policy_id = 1)
I tried this but is not working:
#controller
#policy = Policy.find(:all,:joins => :client ,:conditions => ['raz_soc LIKE ?',params[:search_raz_soc] ])
#policies= PolicyVehicle.find(:all,:joins => :policy, :conditions => ['policy_id = ?',#policy ])
#logs
Policy Load (3.1ms) SELECT `policies`.* FROM `policies` INNER JOIN `clients` ON `clients`.id = `policies`.client_id WHERE (raz_soc = 'MARATEX SAC')
PolicyVehicle Load (0.3ms) SELECT `policy_vehicles`.* FROM `policy_vehicles` INNER JOIN `policies` ON `policies`.id = `policy_vehicles`.policy_id WHERE (policy_id = 70353610714140)
I'm trying to do something like this
select * from policy_vehicles where policy_id
IN ( SELECT id FROM policies WHERE
client_id IN (SELECT id FROM clients raz_soc = ?) )
Can someone can help me please?
Try this (I have explained what the queries return so make sure that this is what you want from them)
#policies = Policy.find(:all,:joins => :client ,
:conditions => ['raz_soc LIKE ?',"%#{params[:search_raz_soc]}%"] )
# returns an array of policy records based on the raz_soc condition on client
#policy_vehicles = PolicyVehicle.find(:all,:joins => :policy,
:conditions => ['policy_id IN (?)',#policies] )
# returns an array of policy_vehicles that are associated with any record in the #policies array
Other option is to do it in a single query as:
#policy_vehicles = PolicyVehicle.joins(:policy => :client).
where('clients.raz_soc LIKE ?',"%#{params[:search_raz_soc]}%")

Resources