Edit: it turns out I made a very simple mistake and had a Template that was associated with a LocalTemplate id that no longer existed. If anyone has this problem and thinks that they somehow are unable to unable to associate the id of another model in their update action, make sure that you didn't accidentally delete the parent object causing that id to no longer exist!
The code below, while dramatically simplified did work for me.
I have a Template model in my rails app. It has a method "data" defined in it.
I am able to access this method in the create and show actions with #template.data, however when using the same #template.data in the update action of my controller I get a no method error because I am not showing the correct local template id to it. This line can be found in the model where it reads base_data = YAML.load(local_template.data)
I stored an id of the associated local_template when initially saving a new template, but how can I make sure I reference that id again in the update action so I do not get a no method error?
Here is a simplified version of the Template model and controller
Model:
class Template < ActiveRecord::Base
def data
base_data = YAML.load(local_template.data)
# couldn't pass the correct LocalTemplate here because
# the local_template_id I had in my Template model no
# longer existed. Changing the id to a LocalTemplate
# that did exist fixed the issue.
end
end
Controller:
class TemplatesController < ApplicationController
def index
#business = Business.find(params[:business_id])
#templates = #business.templates.all
end
def new
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build
end
def create
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.build(template_params)
if #template.save
#template.data #works fine here
redirect_to business_url(#template.business_id)
else
render 'new'
end
end
def show
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
#template.data #works fine here too
end
def edit
#business = Business.find(params[:business_id])
#local_templates = LocalTemplate.all
#template = #business.templates.find(params[:id])
end
def update
#business = Business.find(params[:business_id])
#template = #business.templates.find(params[:id])
if #template.update_attributes!(pass_template_params)
Api.new.update_template(#template.data.to_json) #this is where I had a problem
redirect_to business_url(#template.business_id)
else
render 'edit'
end
end
end
You are mixing a lot. There is a lot to refactor in your controller...
First of all, your TemplatesController should be about the template resources, but your controller looks more like a BusinessesController. In general your update action for example should look more like:
def update
#template = Template.find params[:id]
#template.attributes = template_params # though this should raise a NoMethodError, because you dind't define it; I'd prefer params[:template] if possible
if #template.save
redirect_to business_url(#template.business_id)
else
#local_templates = LocalTemplate.all
render 'edit'
end
end
Instantiating #business and #local_templates makes non sense, because you don't use it at all. Speed up your responses if you can! :)
Fixed that, there is no need for the overhead of a nested resource in update (as you did).
If saving #template fails for validation reasons, you better should load the business object late by:
#template.business
in your /templates/edit.html.erb partial. Then you also do not need a nested route to your edit action... You see, it cleans up a lot.
As a general guideline you should create as less as possible controller instance variables.
If you cleaned up your controller and views, debugging your data issue will be easier.
I assume:
local_template
in your Template model to be an associated LocalTemplate model object. So it should no issue to call that anywhere if you ensured the referenced object exists:
class Template < ActiveRecord::Base
def data
return if local_template.nil?
YAML.load(local_template.data)
end
end
or validate the existence of the local_template object. or even b
You should confirm #template is not nil, if #template is nil, you can't use data method.
1.9.3-p547 :024 > nil.data
NoMethodError: undefined method `data' for nil:NilClass
from (irb):24
from /Users/tap4fun/.rvm/rubies/ruby-1.9.3-p547/bin/irb:12:in `<main>'
And you should use update_attributes!, it can raise an exception if record is invalid.
You can do like this.
if #template
#template.update_attributes!(template_params)
#template.data
end
Related
Rails 5.2
In my inventories_controller.rb, I have the following:
before_action :fetch_product, only: [:show]
def show
........
end
def fetch_product
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
This works fine, when I do: http://0.0.0.0:3000/sku/12345678
I am trying to implement search functionality, so I modified nventories_controller.rb as follows:
def fetch_product
if params.has_key?(:search) && !params[:search].blank?
product = Product.find_by_sku(params[:search])
if !product
params = params.except[:search]
redirect_to product_show_path, alert: 'Product was not found'
end
params = params.merge!(:sku, product.sku)
end
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
When I do: http://0.0.0.0:3000/sku/12345678
I get an instant error message:
undefined method `has_key?' for nil:NilClass
Using my debugger, I find that on entry into the fetch_product method, params is nil
Any idea what's going on?
params = params.merge!(:sku, product.sku) modifies the hash in place and returns nil, don't do that assignment, just call params.merge! (if you still want to do the assignment, remove the "!").
Personally, I wouldn't modify the params hash unless it's really really needed, I would use another variable.
I'm trying to create and order that is associated with an item.
An Order has one item:
class Order < ActiveRecord::Base
has_one :item
end
An Item belongs to an order:
class Item < ActiveRecord::Base
belongs_to :user
end
According to the guide this should work:
build_association(attributes = {})
create_association(attributes = {})
I have this in my controller:
def create
#order = #current_item.build_order(order_params)
#order.save
redirect_to #order
end
And this is the error I'm getting:
undefined method `build_order' for nil:NilClass
I know this has to do with how I've defined current_items but I've tried many different things and all lead to this same error message.
I have this in my application helper:
def current_item
Item.find(params[:id])
end
Can anyone point me in a better direction for how to define this or what I'm doing wrong here. Thanks for your help!
1) You don't have access to a helper method from the controller. You can include the helper class in your controller but it's a really bad practice. You must use helper methods only in the views.
2) You can move current_item method from the helper to the controller. Then there will be another problem. In your create method, you are trying to access instance variable #current_item which is not initialized at the moment, not the method. You can do it this way:
#order = #current_item.build_order(order_params)
to
#order = current_item.build_order(order_params)
Then current_item will return you Item object.
3) I am not sure what are your params, but you can implement it this way:
def create
#order = Order.new(params[:order])
#order.save
redirect_to #order
end
where params[:order] is for example:
{name: "order 1", item_id: 1}
You should change your create to use a method, rather a variable, so modify it as follows:
def create
#order = current_item.build_order(order_params)
#order.save
redirect_to #order
end
# rest of code
def current_item
Item.find(params[:id])
end
This should help.
Good luck!
The error you're getting is being caused by trying to run Item.find(params[:id]) but not passing it a valid value. It seems that params[:id] is maybe nil? Can you confirm this using a debugger or by temporarily adding raise "Params[:id] is set to #{params[:id]} to the first line of the method, running the code and seeing what it says in the terminal output?
All you need to do make this work is have a parameter value for the item come from the form that is being submitted. Normally rails uses the route/url to populate the value of params[:id]. For example, when the request is GET /items/1, params[:id] is 1.
In this case though, unless you've done some custom routing that you haven't shown in your question, creating a new order would usually be a POST to /orders and since there is no id in the url, params[:id] is nil.
It's up to you to add the item id from the order form. It would make sense that it would be sent with the rest of the order params as item_id, rather than just id, since id is usually used to reference the current object, which is a new order and therefore doesn't get have an id.
You'll need to make sure that item_id is whitelisted in your strong params with the rest of the values in the order_params method (I assume you defined this in the same controller but did not show it in the code), and then the code would look something like this.
def create
#order = current_item.build_order(order_params)
#order.save
redirect_to #order
end
#note the changes the the argument
def current_item
Item.find(order_params[:item_id])
end
def order_params
params.require(:order).permit(:item_id, :other_values_that_you_send)
end
In my controller I am trying to move my create method into a background process, as it takes too much time. I can't get it right.
My option 1:
First I went with creating a self method for my controller so it looked like this:
def create
MyController.delay.create_and_email(current_app_user)
respond_to do |format|
format.js {}
end
end
def create_and_email(user)
#user = AppUser.find_by(id: user.id)
#link_share = #user.link_shares.build(link_share_params)
#link_share.save
end
I had to throw in the current_app_user as the self method could not see it, but I got totally puzzled on what to do with my link_share strong params, because the self method does not see the link_share_params. I guess I could make this option work, if anyone would point me how to throw in strong params into a self. controller method.
My option 2:
I then decided to move the whole action into my User model like this
Contoller:
def create
#user = current_app_user
#user.delay.create_and_email(link_share_params)
respond_to do |format|
format.js {}
end
end
Model:
def create_and_email
link_share = self.link_shares.build(**params**)
link_share.save
end
Again, trouble with the strong params. How would I go with importing my params into the model? Should I just chain them as arguments?
Any advice appreciated
I'm creating an action which duplicates an item and then allows the user to edit it and save it back to the database.
I've written the following method in my controller and it mostly works apart from the Paperclip attachment which won't move across for some reason.
def duplicate
existing_event = Event.find(params[:id])
#event = Event.new(existing_event.attributes)
render action: 'new'
end
I've seen this question where the person is using .dup but I can't seem to get that working in a situation where the user edits the new item before saving.
I also tried using something like #event.image = existing_event.image but that didn't have any effect either.
This is what my create method looks like:
def create
#event = Event.create(event_params)
if #event.save
redirect_to events_path, notice: "Event was successfully created."
else
render action: 'new'
end
end
If it makes any difference I'm using S3 for the image uploads too and it doesn't really matter to me if there are multiple copies of the image up there.
Can anyone help? Thanks!
Passing the attachment params does just that: pass the params.
You need to pass the file itself.
Below you get the idea how to do it, not tested it, but you can play around it and make it work, it shouldn't be that hard.
On new action:
existing_event = Event.find(params[:id])
#event = Event.new(existing_event.attributes)
#event.image = File.open(existing_event.image.path,'rb')
render :action => 'new'
Also:
Check in your create action, you have a slight mistake, calling create and save for the same record - this is redundant. You should call #event=Event.new(event_params) and then if #event.save.
Here's a little snippet I use in an initialiser:
module Paperclip
class HasAttachedFile
def define_with_extensions
define_without_extensions
define_dup_override
end
alias_method_chain :define, :extensions
private
def define_dup_override
name = #name
#klass.send :define_method, "dup" do
copy = super()
self.class.attachment_definitions.each do |name, options|
ivar = "#attachment_#{name}"
copy.instance_variable_set(ivar, nil)
copy.send(name).assign send(name)
end
copy
end
end
end
end
This will assign the files from the old record to the new record programatically without knowing what the actual attachment definitions are.
I currently have two models School and Course where School has_many courses, and Course belongs_to school. Additionally, School and Course are nested resources, where School is the parent resource, and Course the child.
I have created several test records in the Rails Console so that a query such as when the child calls upon the parent Course.first.school successfully executes and returns all the relevant information of the school Course.first is associated with.
However, when put into a controller function, I would instead get an error "undefined method `school' for nil:NilClass" for the following line:
redirect_to school_course_path(#course.school, #course)
.. as if the .school part wasn't recognized (where as it was in the console). Why is this the case, and how do I get past this error? Thanks!
Edit - as suggested, it could be that my #course instance variable isn't passed from method to method in the controller. I have attempted at passing them through via a private method, but its still giving me the same error. Here is my code (background: the model Question belongs_to Course, with Course having many questions. Course isn't part of the nested routes)
class QuestionsController < ApplicationController
def new
#course = Course.find(params[:course]) #confirmed working
self.current_course = #course #I attempt to set current_course, a private method
#question = Question.new
end
def create
#question = Question.new(params[:question]) #also works, in rails console all the questions confirms to have rails id
if #question.save
redirect_to school_course_path(current_course.school, current_course) #source of my frustrations - continues to returns same error message
else
render 'new'
end
end
private
def current_course=(course)
#current_school = course
end
def current_course
#current_course
end
end
Should work if your relationships are set up the way I think they are:
def create
#question = Question.new(params[:question])
#course = #question.course
if #question.save
redirect_to school_course_path(#course.school, #course)
else
render 'new'
end
end
Make sure you have something like this in your create action:
#course = Course.new(params[:course])
your code is okay, it seems there is problem in your redirect.. redirect it to root_path and check whether it is working??