can't update field in rails 4 - ruby-on-rails

When i update my revisor_id from my Petition model, i want to automatically update my revisor_id from my Post model.
So, in my petitions_controller.rb i added this line within the update method:
#petition.post.revisor_id = #petition.revisor_id
With the debugger i can see that after the line is executed, both are set correctly. But it seems like it's not saved in the database or something, because when i request to show all the posts with revisor_id set to 1, it doesnt show anything.
I believe it has something to do with strong parameters since i just chaged to rails 4, but not sure.
Any idea?
#petitions_controller.rb
...
def update
respond_to do |format|
if #petition.update(petition_params)
#petition.post.revisor_id = #petition.revisor_id
format.html { redirect_to #petition, notice: 'Petition was successfully updated.' }
format.json { render :show, status: :ok, location: #petition }
else
format.html { render :edit }
format.json { render json: #petition.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_petition
#petition = Petition.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def petition_params
params.require(:petition).permit(:user_id, :category_id, :revisor_id, :status, :post_id)
end

You're not calling save.
#petition.post.update_attibute(:revisor_id, #petition.revisor_id)
Or
#petition.post.revisor_id = #petition.revisor_id
#petition.post.save
But it looks like you could redesign your database and you has_one through so you wouldn't need to do that.

Related

Rails Pundit unable to find policy of nil only on Create action after adding restrictions on some params

I've modified one of my Goal.rb model to only allow one field to be edited by admins. This model and all actions within it was working prior to this update. This update also works for the edit action, but on the create action returns
Pundit::NotDefinedError in GoalsController#create
unable to find policy of nil
I'm hoping this is just a syntax error, I don't understand why it's not working as no model/policy file names have been changed, only the params and policy settings (which work for the edit action)
I thought it might be because I'm calling the policy on #goal while creating it, but how else would I check permissions there, if not this way?
goals_controller.rb:
def create
#goal = Goal.new(permitted_attributes(#goal))
#stuff
authorize #goal
respond_to do |format|
if #goal.save
# format.json { render :show, status: :created, location: #goal }
else
# format.json { render json: #goal.errors, status: :unprocessable_entity }
end
end
end
def update
authorize #goal
update_params = permitted_attributes(#goal)
#
end
respond_to do |format|
if #goal.update(update_params)
# format.json { render :show, status: :ok, location: #goal }
else
# format.json { render json: #goal.errors, status: :unprocessable_entity }
end
end
end
I just realised, you can call permissions on a new class. I had read this three times, but only after posting the question did it click.
I was trying to check the allowed params of #goal, which didn't yet exist and was showing a nil policy, but with Pundit you can run a check against the allowed params of a Class, in this case:
#goal = Goal.new(permitted_attributes(Goal))

Editing Triple Nested Fields is Creating Duplicates Rails 5.1

I have a triple nested resource, which I am able to create new values for perfectly well. However when trying to edit the record, I am getting duplicated fields for the nested values, which is then creating multiple entries.
I am multiplying the nested fields by 3.
def new
#roast = Roast.new
3.times {#roast.countries.build.regions.build}
end
Edit method:
def edit
#roast = Roast.friendly.find(params[:id])
3.times {#roast.countries.build.regions.build}
end
Should I be removing the 'build' element here? I do want the user to be able to add new values if required however.
And create has nothing special for this:
def create
#roast = Roast.new(roast_params)
respond_to do |format|
if #roast.save
format.html { redirect_to #roast, notice: 'Roast was successfully created.' }
format.json { render :show, status: :created, location: #roast }
else
format.html { render :new }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
I obviously want the 3 nested fields to show on the edit page, but what am I doing wrong for it to keep repeating.
You don't need to add countries or regions in the edit. Just find the Roast.
#This is wrong
3.times {#roast.countries.build.regions.build}
When you edit a Roast, you can access its countries through #roast.countries
If you want, you can define an instance variable to use in the form (although not needed) #countries = #roast.countries

rails issue with naming convention and table

I have an issue with rails with naming convention.
I have a database where i can't rename table so names are not in plural with inflector.
Today i wanted create model and controller for the table "wishlist__c" and the issue is here. I tried 3 times first by duplicating product model, controller.... and changing name then creating files myself and i still got the issue and then with rails g scaffold wishlist__c
The first error when i try to go to url:8080/wishlist__c/index :
Routing Error
uninitialized constant WishlistCController
wishlist__c_controller.rb exist. I notice after many test that the double '__' is a problem in rails. I rename it to wishlist_c_controller and the same with the model. the error message change to
--Solution: I forget to rename folder wishlist__c to wishlist_c in views folder
Thanks you all ! --
ActiveRecord::RecordNotFound in WishlistCController#show
Couldn't find WishlistC with 'id'=index
the code display under this is from wishlist_c_controller.rb:
def set_wishlist__c
#wishlist__c = ::WishlistC.find(params[:id])
end
How to solve it. I need to link my app to this table
edit:
Model wishlist_c.rb:
class WishlistC < ApplicationRecord
self.table_name = "wishlist__c"
end
wishlist_c_controller:
class WishlistCController < ApplicationController
before_action :set_wishlist__c, only: [:show, :edit, :update, :destroy]
# GET /wishlist__c
# GET /wishlist__c.json
def index
#wishlist__c = WishlistC.all
end
# GET /wishlist__c/1
# GET /wishlist__c/1.json
def show
end
# GET /wishlist__c/new
def new
#wishlist__c = WishlistC.new
end
# GET /wishlist__c/1/edit
def edit
end
# POST /wishlist__c
# POST /wishlist__c.json
def create
#wishlist__c = WishlistC.new(wishlist__c_params)
respond_to do |format|
if #wishlist__c.save
format.html { redirect_to #wishlist__c, notice: 'Wishlist c was successfully created.' }
format.json { render :show, status: :created, location: #wishlist__c }
else
format.html { render :new }
format.json { render json: #wishlist__c.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /wishlist__c/1
# PATCH/PUT /wishlist__c/1.json
def update
respond_to do |format|
if #wishlist__c.update(wishlist__c_params)
format.html { redirect_to #wishlist__c, notice: 'Wishlist c was successfully updated.' }
format.json { render :show, status: :ok, location: #wishlist__c }
else
format.html { render :edit }
format.json { render json: #wishlist__c.errors, status: :unprocessable_entity }
end
end
end
# DELETE /wishlist__c/1
# DELETE /wishlist__c/1.json
def destroy
#wishlist__c.destroy
respond_to do |format|
format.html { redirect_to wishlist__c_index_url, notice: 'Wishlist c was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_wishlist__c
#wishlist__c = WishlistC.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def wishlist__c_params
params.fetch(:wishlist__c, {})
end
end
Rails create RESTful routes based on controller and model. One of the routes would be get wishlist__c/:id which gets mapped to action show of WishlistCController. so when you hit the URL wishlist__c/index it takes index as the id.
If you want to render index page, create a route get wishlist__c/index and map it to index method of your controller. For the above to work you must hit the URL url:8080/wishlist__c/1 where 1 is your WishList ID. Replace it with values of id column of wishlist__c table.
Looking at your controller, you already have a route get wishlist__c/ mapped to the index method of your controller. So url:8080/wishlist__c/ should render index page for your model.

How to get name of model linked from another model in Rails?

I have set up 2 models in Rails:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :platforms
end
and
class Platform < ActiveRecord::Base
attr_accessible :name, :url, :country
validates :name, :presence => true, :length => { :minimum => 5 }
validates :url, :presence => true, :length => { :minimum => 5 }
belongs_to :categories
end
This is my platform controller :
class PlatformsController < ApplicationController
# GET /platforms
# GET /platforms.json
def index
#platforms = Platform.all
#categories = Category.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #platforms }
end
end
# GET /platforms/1
# GET /platforms/1.json
def show
#platform = Platform.find(params[:id])
#categories = Platform.categories
respond_to do |format|
format.html # show.html.erb
format.json { render json: #platform }
end
end
# GET /platforms/new
# GET /platforms/new.json
def new
#platform = Platform.new
#categories = Category.all
respond_to do |format|
format.html # new.html.erb
format.json { render json: #platform }
end
end
# GET /platforms/1/edit
def edit
#platform = Platform.find(params[:id])
#categories = Category.find(:all)
end
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
##categories = Category.new(params[:name])
#categories = #platform.categories.create(params[:categories])
respond_to do |format|
if #platform.save
format.html { redirect_to #platform, notice: 'Platform was successfully created.' }
format.json { render json: #platform, status: :created, location: #platform }
else
format.html { render action: "new" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
# PUT /platforms/1
# PUT /platforms/1.json
def update
#platform = Platform.find(params[:id])
#categories = Category.find(:all)
respond_to do |format|
if #platform.update_attributes(params[:platform])
format.html { redirect_to #platform, notice: 'Platform was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
# DELETE /platforms/1
# DELETE /platforms/1.json
def destroy
#platform = Platform.find(params[:id])
#platform.destroy
respond_to do |format|
format.html { redirect_to platforms_url }
format.json { head :no_content }
end
end
end
I do not understand what I do wrong, but it doesnt correctly assign categories to platforms, and also in the platforms index view, when I try to use :
<%= platform.categories %>
it gives me error cannot find Category with id= "and here the respective id"
I am really confused since I followed tutorial for this one.
I use Rails 3.2.8
Without your view, I can't say for sure what it is you're trying to do exactly. Most importantly, what is in your params[:categories] hash? Given the name, it sounds like you intended for it to be multiple categories. However, your code is written as if you intended it to be a single set of attributes which describe one Category.
Since I can't say for sure what you want to do, I'll answer your question by explaining what you are doing. Maybe that will help you figure out how to fix it.
Your create code currently looks like this:
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
##categories = Category.new(params[:name])
#categories = #platform.categories.create(params[:categories])
The first line creates the new Platform and is easy. Skipping over the comment to the third line. This is probably what's tripping you up.
You are selecting the associations for your newly created Platform and trying to create a new category with attributes as stored in the params[:categories] hash. I'm afraid this is not allowed. (I think it throws an ActiveRecord::RecordNotSaved exception, but I could be wrong.) You can not create on a #platform which hasn't been persisted yet. Instead, I think you want build.
Here is the relevant documentation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
The difference between create and build is that build just sets up the association without actually saving it to the database yet. create saves it immediately. The nice thing about build is that you don't actually have to save it yourself. It tags along for free when you call #platform.save or #platform.update_attributes. Also, save is automatically wrapped in a transaction, so it won't create the new Category if it fails to create the new Platform for whatever reason.
The next interesting thing is that you are assigning the result of your create to #categories. I don't think this is what you want either. You don't need to save the new Category because it tags along with your #platform. However, if the save of the platform fails, then you are going to re-render your new view with this value of #categories whereas in new you set #categories = Category.all. This could certainly cause some confusion on the new view after a failed create.
In summary, I think your create code should look something like the following.
# POST /platforms
# POST /platforms.json
def create
#platform = Platform.new(params[:platform])
#platform.categories.build(params[:categories])
respond_to do |format|
if #platform.save
format.html { redirect_to #platform, notice: 'Platform was successfully created.' }
format.json { render json: #platform, status: :created, location: #platform }
else
#categories = Category.all
format.html { render action: "new" }
format.json { render json: #platform.errors, status: :unprocessable_entity }
end
end
end
If you're params[:categories] is not a hash of category attributes and is actually a comma delimited string of category names, then you would want to do something like the following instead of my second line above:
params[:categories].split(",").each do |category|
#project.categories.build(name: category)
end
You may also want to check out accepts_nested_attributes_for which can DRY out your controller even more.
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I hope that helps.

How do I call update action from another action in rails 3?

So I'm writing a basic member modifying action, and I figured, lets stay DRY and just modify the params hash then pass along to our update method but it doesn't seem to work. I guess there is some rails magic going on that I can't find... From what I've read this should work. I'm using Rails 3.2.
Here's an example of what I'm trying to do:
# POST /tasks/1/toggle_done
def toggle_done
#task = Task.find(params[:id])
puts "<<<<<", params
# invert done bool value
params[:done] = !(#task.done)
# thought maybe update_attributes retured a full set of
# attributes in the params...
#params[:name] = #task.name + "...test."
# thought maybe the method call to update was getting
# filtered or something. Doesn't seem to help.
#params[:_method] = "put"
# redirect to update with these new params
puts ">>>>>", params
# Why bother rewriting task.done = x; task.save;
# redirect_to show; etc when update already does that.
update
end
# PUT /tasks/1
# PUT /tasks/1.json
def update
#task = Task.find(params[:id])
puts "======", params
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
I get the following console output:
<<<<<
{"_method"=>"post", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1"}
>>>>>
{"_method"=>"put", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1", "done"=>false, "name"=>"Put Done button in index view...test."}
======
{"_method"=>"put", "authenticity_token"=>"CVqzsJfSVgM7Bq/kXlrjzkWVoA7Pbne4GNEHqbQB42s=", "action"=>"toggle_done", "controller"=>"tasks", "id"=>"1", "done"=>false, "name"=>"Put Done button in index view...test."}
So it seems like the params array is set right. It renders the regular show view with the flash message "Task was successfully updated.", so it seems like the whole method gets executed but non of the model properties are getting changed. I guess something inside update_attributes is failing. Can anyone shed some light on this for me?
Also is this a crazy thing to do? Should I be setting and saving inside my toggle_done method instead of chaining to update?
Rails saves the attributes for the task object in the hash params[:task]. So you in your toggle_done method you need to save the result in params[:task][:done] otherwise rails cannot associate the done attribute with the task.
def toggle_done
#task = Task.find(params[:id])
params[:task] = { done: !(#task.done) }
update
end
But with calling the update method you make 3 database queries where only 2 are neccessary - And the first 2 are identically because you load the Task with the ID in the toggle_done method as well as in update.
To avoid this you can put the save and redirect part into a protected method and call it when you want to save it. Like this:
def toggle_done
#task = Task.find(params[:id])
params[:task] = { done: !(#task.done) }
save_updated
end
def update
#task = Task.find(params[:id])
save_updated
end
protected
def save_updated
respond_to do |format|
if #task.update_attributes(params[:task])
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
You're passing params[:task] to update_attributes, which doesn't exist. Try:
params[:task] = {:done => !(#task.done)}

Resources