Rails form adding empty entry via model - ruby-on-rails

I am new to ruby, trying to follow the official documentation and create a basic form for creating a post:
<%= form_for #post, :url => { :action => "create" }, :html => {:class => "nifty_form"} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :entry, :size => "60x12" %>
<%= f.submit "Create" %>
<% end %>
The form is successfully adding an entry to the database, but an empty one, I think I must be missing something in my controller? Do I need to pass the variables somehow?
def create
#post = Main.create
end

A basic create action can look like this. You first initialize a new post. Depending on if it successfully saves you proceed.
# app/controllers/posts_controller.rb
class PostsController < ActionController::Base
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post, notice: 'Post has been created.'
else
render :new
end
end
end
You can shorten your form.
<%= form_for #post do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.text_area :entry, :size => "60x12" %>
<%= f.submit %>
<% end %>
You can see excellent example code along these lines when you generate a scaffold, so I would encourage you to try $ rails generate scaffold Post title body:text and learn by example.

Submitting a form passes the values entered into that form (along with some other information) to the controller as a hash called "params" - the params will contain a block labelled with the name of the form, in this case "post".
You need to use the post block from params in the creation of the new object.
def create
#post = Main.new(params[:post])
if #post.save
# handles a successful save
else
# handles validation failure
end
end

Try:
#post = Main.new(params[:post])
#post.save

Related

Rails form rendering wrong URL after validation errors (not keeping passed parameter)

On a project show page, I pass a very simple parameter on my 'create new task' that stores which project its from:
#project.id), :class => "btn btn-info col-md-12" %>
so that when i create a new task for it, it stores it in the URL on my new task form like this:
http://localhost:3000/task/new?project_id=5
My New form is as follows:
<div class="container sign-in-register">
<div class="authform">
<%= form_for #task, :html => {:multipart => true} do |f| %>
<h3>Add a task for this project...</h3><br/>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= hidden_field_tag 'project_id', #project_id %>
<%= f.fields_for :taskrelationships do |ff| %>
<%= ff.hidden_field :taskproject_id, value: #project_id %>
<%= ff.label :task_url %>
<%= ff.text_field :task_url, class: 'form-control' %>
<% end %>
<br clear="all">
<%= f.submit "Save Task", class: "btn btn btn-info" %>
<% end %>
</div>
</div>
As you can see i'm using nested attributes in the form (I am creating both a task and a TaskRelationship. Now, when I try to save without filling out all the required fields a validation is thrown but for some reason it redirects me to:
http://localhost:3000/tasks
instead of the original:
http://localhost:3000/tasks/new?project_id=5
I have read many posts and none seem to answer this particular case. The stackO post below was close but when I try it with task instead of user it still cannot find the task_ID
Render error on new page with customize url like http://localhost:3000/education_informations/new?user_id=10
How can I have rails simply render the same exact url as I started with - it seems like this should be easy so must be missing something small.
My controller actions:
def new
#project_id = params[:project_id]
#task = Task.new
#task.taskrelationships.build
end
def create
#project = Project.find(params[:project_id])
#task = Task.new(task_params)
if #task.save
flash[:success] = "This task has been added."
#task.taskrelationships.create!(#taskrelationships_params)
redirect_to tasks_project_path(#project)
else
#task.taskrelationships.build(#taskrelationships_params)
flash[:alert] = #task.errors.full_messages.to_sentence
render :new
end
end
private
def task_params
#taskrelationships_params = params.require(:task).permit(taskrelationships_attributes: [
:task_url,
:taskproject_id
])[:taskrelationships_attributes]["0"]
params[:task].delete(:taskrelationships_attributes)
params.require(:task).permit(
:name,
:user_id,
taskrelationships_attributes: [
:task_url,
:taskproject_id
]
).merge(owner: current_user)
end
UPDATE W / ROUTES
resources :projects do
resources :reviews, except: [:destroy]
member do
get :tasks
end
end
resources :tasks
resources :taskrelationships, only: [:create, :destroy] do
post :vote, on: :member, controller: "task_relationships"
end
thanks for any assistance...
Ok firstly an explanation as to what is going on here:
When you invoke http://localhost:3000/task/new?project_id=5 you are actually being routed to the new action on the task controller (with a project_id param).
Your new action then sets the variables and rails will render the new.html.erb which contains your new task form.
When you submit the form it actually is doing a http POST to /tasks, which routes to the create action of your tasks controller. That url and http method is a result of what is generated from the form_for helper:
<%= form_for #task, :html => {:multipart => true} do |f| %>
This is why the url changes from /tasks/new?project_id=5 to /tasks
Now the create action if it fails the validation simply renders the new form - it is not redirecting anywhere - the url remains unchanged from what it was when it entered this action - meaning, it remains as /tasks.
You do not actually need to navigate to /tasks/new?project_id=5 to render the new form but what you do need to do is set #project_id in the controller so the view has access to that variable (just as it does in the new action):
def create
#project = Project.find(params[:project_id])
#task = Task.new(task_params)
if #task.save
flash[:success] = "This task has been added."
#task.taskrelationships.create!(#taskrelationships_params)
redirect_to tasks_project_path(#project)
else
#task.taskrelationships.build(#taskrelationships_params)
#project_id = #project.id
flash[:alert] = #task.errors.full_messages.to_sentence
render :new
end
end
So, to clarify the change in url is not a redirection it's just that the form is posting to a different url than /tasks/new, and this is actually just a cosmetic issue.
Now if it is a concern to you, you can change the routing to something like the following:
resources :tasks, except: [:create, :new]
post 'new_task' => 'tasks#create'
get 'new_task' => 'tasks#new'
This is mapping the POST and GET http methods to /new_task so the url appears the same for the new and create action invocations. Note you do need to change the url in the form_for helper to use this new route:
<%= form_for #task, url: 'new_task', multipart: true do |f| %>
Since Rails default behaviour in forms is with remote: true, you can move the content of the form to a partial(let's name it _my_form.html.erb), add to the controller action (let's say in create action):
respond_to do |format|
format.js {}
end
Then add a create.js.erb file where you will render the form partial
$("#form").html(
"<%= j render partial: 'my_form', locals: { entity: #entity } %>"
);
Thus, validation errors and all attributes will be accessible inside the form and there is no need to hack the "Rails approach"
I had to adapt and experiment with the currently accepted answer,
and the following ended up working well (using schools rather than tasks):
In config/routes.rb:
resources :schools do
...
end
post 'schools/new' => 'schools#create'
And create the form like:
= simple_form_for [#school], url: 'new' do |f|
Thus the path /schools/new was preserved on validation :)

Issue passing model instance to update action

In my Rails app I'm trying to create a form for updating model instance attributes with new info and am running into trouble.
When I hit submit on the edit form, the following error is thrown:
param is missing or the value is empty: product
And here's the code snippet it provides:
# all the attributes that must be submitted for the product to be listed
def product_params
params.require(:product).permit(:name, :price, :description)
end
end
I think the problem is that the model :product isn't getting passed from the edit form to the update action. Here's the form:
<h1>Edit your listing</h1>
<%= form_for edit_item_path(#product), url: {action: "update"} do |f| %>
<div><%= f.label :name %><br />
<%= f.text_field :name, :placeholder => "Name yourself" %>
</div>
<div><%= f.label :price %><br />
<%= f.number_field :price, :placeholder => "Name your price" %>
</div><br />
<div><%= f.label :description %><br />
<%= f.text_area :description, :cols => "50", :rows => "10", :placeholder => "Write a few sentences about the item you're listing. Is it in good condition? Are there any accessories included?"%>
</div>
<br />
<%= f.submit "Update listing" %>
<% end %>
Here are the edit and update actions in my products_controller:
def edit
#product = Product.find(params[:id])
end
def update
#product = Product.find(params[:id])
respond_to do |format|
if #product.update_attributes(product_params)
format.html {render :action => "show"}
else
format.html {render :action => "edit"}
end
end
end
Finally, my product routes
get "/products/new(.:format)" => "products#new", :as => "list_item"
post "/products/create(.:format)" => "products#create"
get "/products(.:format)" => "products#index"
get "/products/:id(.:format)" => "products#show"
get "/products/:id/edit(.:format)" => "products#edit", :as => "edit_item"
post "/products/:id/update(.:format)" => "products#update"
So anyone know what the problem is? Am I not passing the right info to the update action? If I'm not, what do I need to do to do it?
form_for
The problem you have is you're using form_for without any object
form_for generates an appropriate form tag and yields a form builder
object that knows the model the form is about. Input fields are
created by calling methods defined on the form builder, which means
they are able to generate the appropriate names and default values
corresponding to the model attributes, as well as convenient IDs, et
form_for helpers are primarily designed to give you a way to manage ActiveRecord objects:
<%= form_for #object do |f| %>
...
<% end %>
--
Fix
Everything inside this form block will have to work with the object in the form_for. As you have only used a path helper in your form_for method, it's not going to work as you hope.
You'll need to do this:
<%= form_for #product, url: {action: "update"} do |f| %>
This will ensure your form_for populates the object correctly. The error you have basically says your strong_params method is expecting this structure:
params => {
"product" => {
"name" => ____,
"price" => _____,
"description" => ______
}
}
As you've not included the #product object in your form_for, your params hash won't have the product key, thus causing your error. The fix is to populate the form_for element correctly
Replace
form_for edit_item_path(#product), url: {action: "update"}
with
form_for #product
which is similar to
form_for #product, as: :product, url: product_path(#product), method: :patch do |f|

Keep params after render Ruby on Rails

I have a Project that belongs to User. In my user view I have a link to add a new project, with the parameter for the user I want to add the project to.
<%= link_to 'Add new project', :controller => "project", :action => "new", :id => #user %>
Url: /projects/new?id=62
Adding a project to a user works. The problem is when the validation fails while adding a new project and I do a render.
def create
#project = Project.new(params[:project])
if #project.save
redirect_to :action => "show", :id => #project.id
else
render :action => "new"
end
end
view:
<%= form_for #project do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.hidden_field :user_id , :value => params[:id] %>
<%= f.submit "Create project" %>
<% end %>
routes
resources :users do
resources :projects
end
How can I keep the parameter for the user after the render? Or is there some better way to do this? Been looking at a lot of similar questions but can't get it to work.
try
render :action => "new", :id => #project.id
if its not works for you, then try alternate way to pass the parameter to your render action.
This can also help you->
Rails 3 Render => New with Parameter
You shouldn't use params[:id] to assign value to this form field. Instead, add this to your #new action in controller:
def new
#project = Project.new(user_id: params[:id])
end
and then just write this in your form:
<%= f.hidden_field :user_id %>
Because #project was defined in your #new and #create actions and because it already contains a Project instance with a user_id assigned to it, the value would automatically be added to this field.

Editing has_one objects with form_for in Rails

I have a has_one / belongs_to association between two models -> User and ContactCard. While I am able to create a contact card for a user, whenever I try to edit the card the create action is called from the ContactCardsController rather than update (I can tell because I have different success messages on each). It changes the attributes of the contact card just fine I have to say. I'm mostly happy it's working but would rather patch up any gaps in my understanding of rails paths and associations. What am I missing? Why isn't it using the action I expect? Also if you know of any relevant examples on the web or on github I could study up on, I'm all ears. Thanks!
Contact Cards Controller...
class ContactCardsController < ApplicationController
def create
current_user.build_contact_card(params[:contact_card])
if current_user.contact_card.save
flash[:success] = "Contact Card created!"
redirect_to '/account'
else
flash[:error] = "Fail!"
redirect_to '/account'
end
end
def update
if current_user.contact_card.update_attributes(params[:contact_card])
flash[:success] = "Profile updated."
redirect_to '/account'
else
flash[:error] = "Fail!"
redirect_to '/account'
end
end
Link to edit form...
<%= link_to "Edit Profile", edit_user_contact_card_path(current_user) %>
Edit form...
<%= form_for [current_user, current_user.build_contact_card], :url => user_contact_card_path(current_user) do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<%= f.submit "Save Contact Details", :class => "btn btn-large btn-primary" %>
<% end %>
Relevant Routes...
resources :users do
resource :contact_card
.....
The User model has_one :contact_card and the ContactCard model belongs_to:user
<%= form_for [current_user, current_user.build_contact_card], :url => user_contact_card_path(current_user) do |f| %>
This is building a new contact card every time you edit! Change it to
<%= form_for [current_user,contact_card], :url => user_contact_card_path(current_user) do |f| %>
You'd want the build_contact_card in the create action of User controller probably

attachment_fu and multipart form_for

Woo. My first question.
I have a feeling I'm overlooking something pretty basic in the construction of my form. I'm using attachment_fu and can't get this form to pass anything besides the file data. A user has_many profiles and a profile has_many documents.
My form looks like this:
<%= error_messages_for :document %>
<% form_for([#user, #profile, #document], :html => {:multipart => true }) do |f| -%>
<p>
<label for="document">Upload A document:</label>
<%= f.file_field :uploaded_data %>
</p>
<%= f.label :description%>
<%= f.text_field :description%>
<p>
<%= submit_tag 'Upload' %>
</p>
<% end -%>
And here's the controller:
before_filter :require_user, :get_profile
def new
#document = #profile.documents.build
end
def create
#document = #profile.documents.build(params[:document])
if #document.save
flash[:notice] = 'Your document was successfully created.'
redirect_to document_url(#document)
else
render :action => :new
end
end
private
def get_profile
#user = current_user
#profile = #user.profiles.find(params[:profile_id])
end
The logs show all the image data getting posted, but I cannot pass the description or, more importantly, the profile_id, which is the foreign key in my document model. I was stuck on this all night, and can't think of anything fresh this morning. Any help would be great.
For the profile_id you will need something like:
<%= f.hidden_field :profile_id %>
Which in your controller you will get at using params[:document][:profile_id] if needed.
Although from trying to guess at what your code is doing, I suspect that params[:profile_id] is already set from whatever route got you to this controller.
I am not sure why you aren't seeing anything for the description. It should be coming into your controller as params[:document][:description].

Resources