Rails update model from another controller - ruby-on-rails

So I have this 2 models, Folder and Event. Folder have_one Event and Event belongs to Folder.
I want to be able to update a folder's event with new attributes at the same time as the folder. Something like this.
def update
respond_to do |format|
if #folder.update(folder_params)
#folder.event.update_attributes(status: #folder.status)
format.html { redirect_to #folder, notice: 'Folder was successfully updated.' }
format.json { render :show, status: :ok, location: #folder }
else
format.html { :edit }
format.json { render json: #folder.errors, status: :unprocessable_entity }
end
end
end
But I understand this is not very MVC compliant and also does not work. So any idea on how I can make this work ?
Thanks in advance

You can do something like:
class Folder < ActiveRecord::Base
after_save :update_event_status, on: update #Or after_update :update_event_status
def update_event_status
event.update_attributes(status: status)
end
end

write method in Model and call it in controller method. like below code.
class Folder < ActiveRecord::Base
def update_event_status(event,status)
event.update_attributes(status: status)
end
end
def update
respond_to do |format|
if #folder.update(folder_params)
update_event_status(#folder.event,#folder.status) #call methode here
format.html { redirect_to #folder, notice: 'Folder was successfully updated.' }
format.json { render :show, status: :ok, location: #folder }
else
format.html { :edit }
format.json { render json: #folder.errors, status: :unprocessable_entity }
end
end
end
Try this.

Related

How to add different edit/update method into rails controller?

The controller:
def edit
end
def update
respond_to do |format|
if #item.update(item_params)
format.html { redirect_to #item, notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
I want to add another methods like this. For updating only 1 column of item and i want to dont show other columns to user.
I have all neccesary views.
def another_edit
end
def another_update
respond_to do |format|
if #item.update(params[:item].permit(:amount))
format.html { redirect_to #item, notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
I thought my routes are wrong. Then tried
resources :items do
collection do
get 'add', on: :edit
end
end
and
get 'items/another_edit'
Is it possible or not? Please help me.
You need to add those endpoints in routes as "member" (not collection):
resources :items do
get :another_edit, on: :member
put :another_update, on: :member
end
Then routes will be built with "id" parameter inside.

Rails: How to update multiple models together, with proper rollback (kind of manual nested attributes)

I (for various reasons) have decided not to use nested_attributes. Here is my current controller:
def update
# Update snippet
#snippet.update(snippet_params)
# Update hotspot
#snippet.hotspot.update(hotspot_params)
# Update text
#snippet.text.update(medium_params_with(:text))
# Update audio
if params[:audio] {
#snippet.audio.destroy
#snippet.media << Audio.new(medium_params_with(:audio))
}
respond_to do |format|
if success = true #fix this up
format.html { redirect_to #project, notice: 'Snippet was successfully updated.' }
format.json { render :show, status: :ok, location: #snippet }
else
format.html { redirect_to #project, warning: 'Highlight was not updated.' }
format.json { render json: #snippet.errors, status: :unprocessable_entity }
end
end
end
This is more or less OK if nothing goes wrong.
How do I wrap this into something like:
try {
update everything
} else {
undo any changes I made while trying to update everything
}
if success {
etc
} else {
etc
}
If what you are changing are ActiveRecord models using a SQL database then http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html might provide exactly what you're looking for.
Following #Jonah's answer, I wrapped the DB changes in ActiveRecord::Base.transaction:
def update
success = ActiveRecord::Base.transaction do
# Update snippet
#snippet.update(snippet_params)
# Update text
#snippet.text.update(medium_params_with(:text))
# Update audio
if params[:audio]
#snippet.audio.destroy
#snippet.media << Audio.new(medium_params_with(:audio))
end
end
respond_to do |format|
if success
format.html { redirect_to #project, notice: 'Snippet was successfully updated.' }
format.json { render :show, status: :ok, location: #snippet }
else
format.html { redirect_to #project, warning: 'Highlight was not updated.' }
format.json { render json: #snippet.errors, status: :unprocessable_entity }
end
end
end

Rails, update the product right after create one

I have the web app on RoR, but the issue is once the user upload the image, the product_id is not associated with the product_attachments. Not until I proceed to next form.
Following were my controller
ProductAttachmentsController:
class ProductAttachmentsController < ApplicationController
before_action :set_product_attachment, only: [:show, :edit, :update, :destroy]
def index
#product_attachments = ProductAttachment.all
end
def show
end
def new
#product_attachment = ProductAttachment.new
end
def create
#product_attachment = ProductAttachment.new(product_attachment_params)
respond_to do |format|
if #product_attachment.save
format.html { redirect_to #product_attachment, notice: 'Product attachment was successfully created.' }
format.json {render :json => #product_attachment}
else
format.html { render :new }
format.json { render json: #product_attachment.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #product_attachment.update(product_attachment_params)
format.html { redirect_to #product_attachment.product, notice: 'Product attachment was successfully updated.' }
format.json { render :show, status: :ok, location: #product_attachment }
else
format.html { render :edit }
format.json { render json: #product_attachment.errors, status: :unprocessable_entity }
end
end
end
def destroy
#product_attachment.destroy
respond_to do |format|
format.html { redirect_to product_attachments_url, notice: 'Product attachment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product_attachment
#product_attachment = ProductAttachment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_attachment_params
params.require(:product_attachment).permit(:product_id, :attachment)
end
end
How can I trick the create method, so it will create a product_id when I create product_attachment? Currently I need to proceed next step and it trigger update method to insert product_id in product_attachments table. Thanks!!
You can store the product attachment id in a session variable
if #product_attachment.save
session[:product_attachment] = #product_attachment.id ### HERE!
format.html { redirect_to #product_attachment, notice: 'Product attachment was successfully created.' }
format.json {render :json => #product_attachment}
else
format.html { render :new }
format.json { render json: #product_attachment.errors, status: :unprocessable_entity }
end
When you create the product (in the ProductsController ) recall the attachment and update the product_id
if #product.save
if session[:product_attachment]
ProductAttachment.find(session[:product_attachment]).update_attribute(:product_id, #product.id)
session[:product_attachment] = nil
end
...
end

Rails - ActionController::Parameters include controller value with form values

I have a session variable (user_id) that I'd like to include as a foreign key on a record the user is inserting. I have the form values all coming through the form submit to my controller's entity.update(params) method without a problem using the default params definition. That code looks like
def brand_params
#brand_params = params.require(:brand).permit(:name, :brand_type, :profile_id)
end
The update method looks like
if #brand.update(brand_params)
format.html { redirect_to #brand, notice: 'Widget was successfully updated.' }
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
Now I'd like to append the :profile_id session variable to the #brand_params and following other threads here, I've tried a setter method:
def set_brand_params(key, val)
if #brand_params != nil
#brand_params[key] = val
end
end
However, calling this, #brand_params is always nil. Trying to directly add to the brand_params hash doesn't work because it's a better method. If there's a better way to meet this (I'd assume common) use case, I'm all ears! Otherwise, I'd like to know why the var is always nil though in this context, at least the brand_params method sees it as defined and with value. I got this solution in Adding a value to ActionController::Parameters on the server side
Here is the update method as requested:
def update
puts "update"
set_brand_params("profile_id", session[:prof])
respond_to do |format|
if #brand.update(brand_params)
format.html { redirect_to #brand, notice: 'Widget was successfully updated.' }
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
end
end
I'm not agree with merge your data with the params. Because you must permit only the fields you expect your user update. In this case you don't want the user update profile_id on brands, and that is a security best practice.
Then brand_params must be:
def brand_params
#brand_params = params.require(:brand).permit(:name, :brand_type)
end
Your method update may look by this:
def update
#brand = Brand.find(params[:id])
#brand.assign_attributes(profile_id: session[:prof])
respond_to do |format|
if #barnd.update(brand_params)
format.html { redirect_to #brand, notice: 'Widget was successfully updated.'}
format.json { render :show, status: :ok, location: #brand }
else
format.html { render :edit }
format.json { render json: #brand.errors, status: :unprocessable_entity }
end
end
end
You don't need the method set_brand_params at all.
If this don't do the trick, please publish the entry controller, and I hope we find the issue.
edit: add respond_to.

Getting ID from an item that's not saved yet

I am saving data to two different models at once. This has successfully been done.
These two models are associated with each other, so one most store the others ID on save. How to I store the questionnaire_contact_id in QuestionnaireResult?
class QuestionnaireResultsController < ApplicationController
def create
#questionnaire_result = QuestionnaireResult.new(params[:questionnaire_result])
#questionnaire_contact = QuestionnaireContact.new(params[:questionnaire_contact])
respond_to do |format|
if #questionnaire_result.save
#questionnaire_contact.save
format.html { redirect_to root_path, notice: 'Questionnaire was successfully submited.' }
format.json { render json: questionnaires_path, status: :created, location: questionnaires_path }
else
format.html { render action: "new" }
format.json { render json: questionnaires_path.errors, status: :unprocessable_entity }
end
end
end
end
You should use activerecord associations:
def create
#questionnaire_result = QuestionnaireResult.new(params[:questionnaire_result])
#questionnaire_contact = #questionnaire_result.questionnaire_contacts.new(params[:questionnaire_contact])
respond_to do |format|
if #questionnaire_result.save #this line will automatically save associated contact
# code
else
# code
end
end
end
Solved, it was as easy as doing this:
class QuestionnaireResultsController < ApplicationController
def create
#questionnaire_result = QuestionnaireResult.new(params[:questionnaire_result])
#questionnaire_contact = QuestionnaireContact.new(params[:questionnaire_contact])
respond_to do |format|
#questionnaire_contact.save
#questionnaire_result.admin_questionnaire_contact_id = #questionnaire_contact.id
if #questionnaire_result.save
format.html { redirect_to root_path, notice: 'Questionnaire was successfully submited.' }
format.json { render json: questionnaires_path, status: :created, location: questionnaires_path }
else
format.html { render action: "new" }
format.json { render json: questionnaires_path.errors, status: :unprocessable_entity }
end
end
end
end

Resources