I am rather new to rails and programming in general. I feel I have picked up the rails MVC and other concepts pretty well but still have a hard time figuring out the syntax of what goes into controller actions. For example when you create a
def edit
end
How do you know how to format the contents/inside of that method.
Thus far I have seen alot of this:
def new
#product = Product.new
end
If I understand this correctly this is creating an instance of the Product Model and putting it into an instance variable that is accessible by the "new" view in products/view
But let's say I want to edit that. My inclination is to do add the following action in the controller:
def edit
#product = Product.edit
end
I'm not sure the syntax Product.edit is correct though, not sure how to differentiate between edit and update either. How do I know what calls on my Model Object when creating instance variables? Is there somewhere online I can go to learn this? I have found no where thus far with a good list of commands I can play with.
def edit
#product = Product.edit
end
should be
def edit
#product = Product.find(params[:id])
end
simple explanation
The edit action (#method) is called when you call e.q localhost:3000/products/1/edit
the 1 is the id of your product which is passed to your controller and can be accessed by using params.
when the user hit edit . It is ussually send the data to update action
def update
#product = Product.find(params[:id])
#product.update(params[:product].permit(:title, :desc))
end
Ok i know i'm not explain it good enough. You really need to read this
http://guides.rubyonrails.org/
To edit something, first you need to have (or get, or create, or etc...) it. In the new method, you just create new instance of Product, this is not necessary, but needs for *form_for* helper, and generally good practice, because it you can use same form for creating and editing. Product.new just creates new product and initialize its fields with default values. Product.find searches product (single) in database by id and returns it. So for editing you first need to find your product, then it will be used to fill fields in editing form, and than in update method you will update it:
def update
target_product_required
#product.assign_attributes(product_params)
if #product.save
redirect_to #product
else
render :edit
end
end
def target_product_required
#product ||= Product.find(params[:id])
end
def product_params
params.require(:product).permit(:title, :description, :price, :available_quantity, :image, :remote_image_url)
end
This is common pattern: target_product_required returns/assigns founded by id in params product to instance variable, product_params returns product specified params. More about this read in http://guides.rubyonrails.org/
Related
I am new to rails and am in the process of entering in information in a form and saving it to a database. I am following a tutorial which may be out of date. I am getting an error on the second line. Am I passing the wrong parameter?
def create
#student = Student.new(params[:student])
if #student.save
redirect_to new_student_path
end
end
I expect the problem is that you need to process the student parameters before passing them to Student.new, so as not to fall foul of the strong parameters system.
Try this:
def create
#student = Student.new(student_parameters)
if #student.save
redirect_to new_student_path
end
end
private
def student_parameters
params.require(:student).permit(:name, :age)
end
replace :name, :age with the list of attributes you want to white list (allow through)
More information about the mass assignment vulnerability that strong parameters mitigates can be found here.
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
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
In my Rails app I have an invoices_controller.rb with these actions:
def new
#invoice = current_user.invoices.build(:project_id => params[:project_id])
#invoice.build_item(current_user)
#invoice.set_number(current_user)
end
def create
#invoice = current_user.invoices.build(params[:invoice])
if #invoice.save
flash[:success] = "Invoice created."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
Essentially, the new method instantiates a new invoice record plus one associated item record.
Now, what sort of method do I need if I want to duplicate an existing invoice?
I am a big fan of Rails's RESTful approach, so I wonder if I should add a new method like
def duplicate
end
or if I can use the existing new method and pass in the values of the invoice to be duplicated there?
What is the best approach and what might that method look like?
Naturally, you can extend RESTful routes and controllers.
To be rally RESTful, it is important to look exactly, what you want.
i.e. if you want a new invoice and use an existing one as a kind of template, then it is comparable to a new action, and the verb should be GET (get the input form). As is it based on an existing invoice, it should reference that object. After that you would create the new invoice in the usual way.
So in you routes:
resources :invoices do
member do
get 'duplicate'
end
end
giving you a route duplicate_invoice GET /invoices/:id/duplicate(.format) invoices#duplicate
So in your view you can say
<%= link_to 'duplicate this', duplicate_invoice_path(#invoice) %>
and in your controller
def duplicate
template = Invoice.find(params[:id])
#invoice= template.duplicate # define in Invoice.duplicate how to create a dup
render action: 'new'
end
If I understand correctly your question you can:
resources :invoices do
collection do
get 'duplicate'
end
end
and with this you can do:
def duplicate
# #invoice = [get the invoice]
#invoice.clone_invoice
render 'edit' # or 'new', depends on your needs
end
clone_invoice could be a custom method which should have a invoice.clone call in your custom method.
If you question if you can use additional methods except REST, you absolutely can. Google, for example, encourage developers to use something, what they call "extended RESTful" on GoogleIO, http://www.youtube.com/watch?v=nyu5ZxGUfgs
So use additional method duplicate, but don't forget about "Thin controllers, fat models" approach to incapsulate your duplicating logic inside model.
What I am trying to do is kinda complicated. Basically I have an order form and my client would like to be able to add and delete fields himself, such as different services you can purchase along with your item. So what I have done is I have made an orders controller and order model along with a field model and fields controller. How would I implement this now? My order model has a has_many :fields and my field model has a belongs_to :order, but aside from that I am stuck on how to implement this. So far in my orders controller i have a new and create method and heres what inside:
def new
#order = Order.new
#maybe i should put something like: #fields = Field.find(:all)
#title = "Order Form"
end
def create
#order = Order.new params[:order]
if #order.save
flash[:notice] = "Your order has been created"
redirect_to root_path
else
#title = "Order Form"
render 'new'
end
end
and in my fields controller I have a show new create edit update functions with nothing in them. What is the best practice to accomplish what I am trying to do?
Thanks in advance guys
You're looking for nested forms.
Check two screencasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
It will even answer your need:
my client would like to be able to add
and delete fields himself