I have two forms in my new view, one is for the product and the other is for fotos.
When I upload the fotos with a select.file field, these are created by Ajax call by a file create.js.erb then when I fill the others fields to the product I've another button to create it. So I have two forms and one way to create each one.
The problem is the ID, the solution that I've found was to create an object before the user enter to the new view, so I have this code:
Product's controller:
def new
#product = current_user.products.create
end
It creates an object nil, now I can create my Foto to that object, like this:
Painting's controller:
def create
#product = Product.last
#painting = #product.paintings.create(params[:painting])
end
The problem is the line "#product = Product.last", I know that it isn't the right solution, because when I try the edit action, and when I try to create new objects it goes to the last product and not to the actual edit product.
How can I find that current product at my new action???
Thanks a lot.
Building a new object (really showing the new form, since #new is a GET request and should not make destructive changes)
def new
#product = current_user.products.build
end
Creating a new object
def create
#product = current_user.products.build(params[:product])
if #product.save
redirect_to #product
else
render :new
end
end
Showing the Edit form for an object
def edit
#product = current_user.products.find(params[:id])
end
Updating an existing product
def update
#product = current_user.products.find(params[:id])
if #product.update_attributes(params[:product])
redirect_to #product
else
render :edit
end
end
You'll notice that the GET requests (new and edit) make no chagnes to the database.
The two destructive requests (PUT and POST) to (update/create) make changes to the database.
What you are doing in general is awkward and probably not the best way to use the new action of a controller.
To answer your question you would need to pass in the ID of the product in your parameters.
Depending how you are submitting your paintings form you need to add the parameter in either the body of the request or the url. that way you would be able to do something like
Painting's controller
def create
#product = Product.find(params[:product_id]
#painting = #product.paintings.create(params[:painting])
end
If you add a code snippet of your views/forms I'll probably be able to help you better.
Related
I have code in my products_controller.rb
def create
#product = Product.new(product_params)
brand = Brand.find(params[:brand_id])
#product.brand = brand
if #product.save
raise "OK"
else
#brand = Brand.find(params[:brand_id])
render 'brands/show'
end
end
After failure of save method, i need to redirect to brands show.html.erb view, which find brand by id and display information about it.
Questions:
Is it right way of doing that
If user manually passed server_id parameter, how can prevent it from executing (SQL injection)? Or how should i check and display error?
How can i display errors in brands/show views outside form ( because i'm storing form in <a data-form="..."> property, so user can't see form until he clicks on link. Even more, i use form_for([#brand,#brand.send(:products).klass.new]))
use redirect_to brands_path(#brand)
My QuizzesController#index action looks like this:
def index
#user = current_user
#quiz = Quiz.create(user_id: current_user.id)
end
My view draws the quiz form fine. It goes to the results/index view as intended. BUT the various attributes of the quiz are NOT updated on the Quiz instance which is pulled from the database, in the QuizzesContoller#update action:
def update
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
('update' is called in this case because the Quiz instance already exists, having been created in the 'index' action).
So, I tried changing the 'update' action to:
def update
#quiz.save
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
But this triggers the error:
undefined method 'save' for nil:NilClass
Why is that? Shouldn't my QuizzesController have access to the #quiz variable as set up in the 'index' action? Can anyone explain what the problem is there?
Others have answered this question, so I thought I would explain why the answer is what it is. In Ruby, variables that begin with the # symbol are instance variables. This means that they are created when a new instance of their parent object is instantiated and are unique to that instance of the object.
Rails based web apps, for the most part, are stateless, meaning that state is not persisted between http requests. In layman terms, the app treats each and every request independent of all other requests. Due to this, the controllers are instanced classes. Every request instantiates a new instance of the controller class.
EDIT:
More I look at your code, you aren't following proper conventions
class QuizzesController < ApplicationController
# GET index: for displaying a list of quizzes
def index
#quizzes = Quiz.where(user_id: current_user.id)
end
# GET show: for getting a single quiz record
def show
#quiz = Quiz.find(params[:id])
end
# GET new: for initializing a new quiz record
def new
#quiz = Quiz.new
end
# POST create: for saving a new quiz record
def create
#quiz = current_user.quizzes.create(quiz_params)
if #quiz.errors
render :new
else
redirect_to #quiz #or whereever
end
end
# GET edit: for initializing existing quiz for update
def edit
#quiz = Quiz.find(params[:id)
end
# PUT/PATCH update: for updating an existing quiz record
def update
#quiz = Quiz.find(params[:id])
if #quiz.update(quiz_params)
redirect_to #quiz # or whereever
else
render :edit
end
# DELETE destroy: for deleting a quiz record
def destroy
Quiz.find(params[:id]).destroy
redirect_to :index # or whereever
end
end
You have not #quiz variable in your update action. Actions in the controller does not have access to variables in other actions.
The QuizzesController instance is not persisted between requests. The real reason instance variables are used in controllers is to pass that variable to the view.
A normal update action would look something like:
def update
#quiz = current_user.quiz # I'm assuming a user has one quiz?
#quiz.update(quiz_params) # Where quiz params takes the posted parameters from your update form
if #quiz.errors.any?
render :edit
else
redirect_to results_path
end
The key is you need to reassign #quiz with each request
I have a form for creating a ticket, which needs an id of a project. This works but not when it comes to validation. If validation won't pass 'render :new' is executed and the project_id doesn't come with it.
I have tried 'redirect_to new_ticket_path(:project_id => params[:ticket][:project_id]) which renders the form again, but the error messages won't show up so it seems that I need to use 'render :new'.
How can I pass the project_id back to the form or reach project_id from the form without passing it?
def new
#ticket = Ticket.new
#id = params[:project_id]
#project = Project.find(#id)
end
def create
#ticket = Ticket.new(params[:ticket].merge(:user_id => current_user.id))
if #ticket.save
redirect_to #ticket
else
render :new <--- will render without the project_id
end
end
That will render just the view for 'new', but will not run the controller action. You'd need to set up your variables for the 'new' view in your 'create' action.
From http://guides.rubyonrails.org/layouts_and_rendering.html#using-render
Using render with :action is a frequent source of confusion for Rails
newcomers. The specified action is used to determine which view to
render, but Rails does not run any of the code for that action in the
controller. Any instance variables that you require in the view must
be set up in the current action before calling render.
The easiest way around this is to change 'new':
def new
#ticket = Ticket.new(:project_id => params[:project_id])
end
and change any references to #project in your 'new' form to #ticket.project. At that point, you shouldn't have to add anything to your 'create' action as long as your form includes a hidden field for the ticket's project id.
The easiest way to get this working (and I would do this anyway) is to nest the task resource under projects. That way you will always have project_id available in params.
# config/routes.rb
resources :projects do
resources :tasks
end
The urls will look like projects/123/tasks/new etc. Take a look at rake routes.
Write project id into a hidden field in your form and you will okay. And don't forget to initialize #id in your create action
def new
#ticket = Ticket.new
#id = params[:project_id]
#project = Project.find(#id)
end
def create
#ticket = Ticket.new(params[:ticket].merge(:user_id => current_user.id))
#id = params[:project_id] # but make sure it is under this key in params
if #ticket.save
redirect_to #ticket
else
render :new <--- will render without the project_id
end
end
and in the form add
<%= hidden_field :project_id, '', value: #id %>
Why don't you use:
flash[:alert] = #ticket.errors.inspect
redirect_to new_ticket_path(:project_id => params[:ticket][:project_id])
A newbie question regarding the ActiveRecord's save method.
If i have this code (as in rails guide):
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post
else
.....
end
end
The save method returns the new created Post object? How, after the code #post.save, rails know how to substitute the redirect_to #post with the proper post_id (1 or 2 or 3 or ....) to build the link?
It's an OOP concept.
So, after create, the #post is updated with the id, and everything just works.
Take a look at this code.
Hope it helps.
I'm going to explain it by presenting another piece of code that should make things easy to understand
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
Now :user contains the following information based on the User model: id, name, email, telephone.
So that means we have actually assigned to #user the above hashes.
The redirect_to will retrieve the hashes that is already stored in #user and display the new profile page.
I hope it helped.
I have a user and profile models and I use the same form to populate them.
In my controller I have the line:
#user.build_profile
I would like to know what does this line do. The relationship between user and profile is one-to-one, and profile belong to user.
I also have a new model called image, I would like to set up a one-to-many relationship with user, using nested attributes. In my new action in the user coltroller, should I use a similar line like the one above?
#user.build_image
The complete new action:
def new
#user = User.new
#user.build_profile
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
build_profile will create an empty profile object(which will belong to #user). Later on in the create action you will call
#user.save
which will save the profile (along with the user) into the database.
http://guides.rubyonrails.org/association_basics.html
-explains it
edit: For a has_many relation you would call
#user.images.build
to create a new image model. This rail cast goes over it
http://railscasts.com/episodes/196-nested-model-form-part-1