Virtual attribute to set published_at for a Post - ruby-on-rails

I have a post model with a datetime field called "published_at". On my form I have a checkbox for an attribute named "publish_now" (see below). I want "published_at" to be set to the Time.now() when the user checks the checkbox for my virtual attribute :publish_now.
<input class="boolean optional" id="post_publish_now" name="post[publish_now]" type="checkbox" value="1" />
Here is my create and update methods in the controller:
def create
#post = Post.new(params[:post])
if current_user
#post.author_id == current_user.id
end
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :author, class_name: "User", foreign_key: "author_id"
attr_accessible :author_id, :content, :published_at, :title, :publish_now
validates_presence_of :content, :title
def publish_now
!published_at.nil?
end
def publish_now=(value)
if value == "1" && published_at.nil?
published_at = Time.now()
end
end
end
This is my best guess of how it should work based on the railscast on virtual attributes, but it's not saving a value for published_at. Any suggestions where the problem might be?
UPDATE: (the form view)
<%= simple_form_for(#post) do |f| %>
<%= f.input :title %>
<%= f.input :content, input_html: { cols: 100, rows: 10, class: "
span5" } %>
<% if #post.published_now == false %>
<%= f.input :publish_now, as: :boolean %>
<% end %>
<%= f.submit %>
<% end %>
UPDATE Example Log:
Started POST "/posts" for 127.0.0.1 at 2012-10-31 14:14:49 -0500
Processing by PostsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zlZ3s18VXwEvONIkk1CZAYESAEAxPP1OKcUtiyEuZgA=", "post"=>{"title"=>"big big love a doodle", "content"=>"aksdfj;skjf;kasjdf; lkj af;lk ;askdfj ;lk ;laf; ksd ;f", "publish_now"=>"1"}, "commit"=>"Create Post"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.2ms) BEGIN
SQL (0.7ms) INSERT INTO "posts" ("author_id", "content", "created_at", "published_at", "title", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["author_id", nil], ["content", "aksdfj;skjf;kasjdf; lkj af;lk ;askdfj ;lk ;laf; ksd ;f"], ["created_at", Wed, 31 Oct 2012 14:14:49 CDT -05:00], ["published_at", nil], ["title", "big big love a doodle"], ["updated_at", Wed, 31 Oct 2012 14:14:49 CDT -05:00]]
(0.4ms) COMMIT
Redirected to http://localhost:3000/posts/5
Completed 302 Found in 18ms (ActiveRecord: 2.1ms)

The error is in this line
published_at = Time.now()
It should be
self.published_at = Time.now()
The parenthesis are also optional.
If you assign to something then Ruby will assume it's a local variable unless you explicitly provide the object, only then can Ruby know it's actually a method you want to call (published_at= method). This is a common gotcha moment in Ruby.
What happens in your code is that a new local variable published_at is created. See http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html at "Using Accessors Within a Class" for more information.
When reading attributes like that you don't need to prefix by self, however, because Ruby will not assume something is a local variable until you assign it. So before published_at=..., Ruby will treat published_at as a method call, after you do published_at=... (without self in front), it will treat published_at as a local variable. Only then you would have to use self.published_at to actually call the method instead of reading the local variable. For the attribute writter (ends with =), you always need to prefix with the object. You can alternatively use the attributes hash, that way you don't need to prefix by self:
attributes["published_at"] = whatever

Related

ActionController::UnknownFormat in MeetingsController#update

I have read countless SO questions about this issue, and can not find a solution.
I have a button that updates Meeting here:
<%= form_for(meeting) do |f| %>
<%= f.hidden_field :accepted, value: true %>
<%= button_tag(type: 'submit', class: "btn_primary") do %>
Accept <svg><use xlink:href="#checkmark"/></svg>
<% end %>
<% end %>
I get an unknown format error, BUT it still updates. What is causing this error?
I have tried removing respond_to do |format| from the update method. which solves the problem for this one button, but then breaks all the other buttons on the platform that calls meetings/update.
class MeetingsController < ApplicationController
respond_to :json, :html
def update
respond_to do |format| **error highlights this line**
if #meeting.update(meeting_params)
format.json { render :show, status: :ok, location: #meeting }
else
format.html { render :edit }
format.json { render json: #meeting.errors, status: :unprocessable_entity }
end
end
end
end
Console:
Started PATCH "/meetings/224" for 127.0.0.1 at 2016-11-11 16:05:27 -0500
Processing by MeetingsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"hK6AoOZuw9DWyKUXw1dXWOSUolooWgBUPnkItUJX5Tm7XvimsHd9518pkqwVvNhUi3L3vlA4OZaJZiAgrbS0Ig==", "meeting"=>{"accepted"=>"true"}, "button"=>"", "id"=>"224"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Meeting Load (0.2ms) SELECT "meetings".* FROM "meetings" WHERE "meetings"."id" = ? LIMIT 1 [["id", 224]]
(0.3ms) begin transaction
SQL (0.5ms) UPDATE "meetings" SET "accepted" = ?, "updated_at" = ? WHERE "meetings"."id" = ? [["accepted", "t"], ["updated_at", "2016-11-11 21:05:27.882521"], ["id", 224]]
(1.3ms) commit transaction
Completed 406 Not Acceptable in 24ms (ActiveRecord: 2.7ms)
ActionController::UnknownFormat (ActionController::UnknownFormat):
app/controllers/meetings_controller.rb:65:in `update'
This was never an issue, and seemingly came out of no where. What is the cause of this error? Thanks!
I think I know what the issue is. I've added a line to your update method
class MeetingsController < ApplicationController
respond_to :json, :html
def update
respond_to do |format| **error highlights this line**
if #meeting.update(meeting_params)
format.html { redirect_to some_rails_path }
format.json { render :show, status: :ok, location: #meeting }
else
format.html { render :edit }
format.json { render json: #meeting.errors, status: :unprocessable_entity }
end
end
end
end
The reason you're getting an unknown format error but still successfully updating meeting is because your method has solid logic, so it's updating the meeting, but then once it does that, and you're only telling it what to do in terms of JSON but not in terms of HTML, you address both in your else statement, but not if the update succeeds.
Is this supposed to be an ajax request? It doesn't seem to be. But you're rendering json instead of html. If you're going to allow the page to reload you need to add an html option to if #meeting.update(meeting_params)
Something like: format.html { redirect_to #meeting, notice: 'Meeting was successfully updated.' }
If you're doing an ajax request this isn't the correct answer, but I don't see any indication in your question that you are. Lmk if you need further clarification.

Rails collection select only saving part of the attributes

I am trying to understand how to do a simple thing - save both an id and the text from a collection_select.
I am just beginning work with RoR so I don't have a good grasp on everything yet.
I have experience mostly in Java plus some Perl, so perhaps I did not choose the correct way.
I have a Defect which has a DefectRootCause.
There is another entity, DefaultRootCauses from which I can choose the DefectRootCause.root_cause to associate with a Defect.
I am trying to save the Defect with its DefectRootCause. The DefectRootCause should have the root_cause field as text, the values chosen from the DefaultRootCause.
The problem is that I am only getting to save one of the root_cause or default_root_cause_id for the DefectRootCause.
More, the DefectRootCause.root_cause gets the id form the DefaultRootCause instead of the text.
Please let me know if I am right in trying to get everything to happen "magically" or if I should do some processing of the data in a controller.
See below for the code.
I have the models:
class Defect < ActiveRecord::Base
has_one :defect_root_cause, :dependent => :destroy
has_many :default_root_cause, :through => :defect_root_cause
end
class DefectRootCause < ActiveRecord::Base
belongs_to :defect
belongs_to :default_root_cause
end
class DefaultRootCause < ActiveRecord::Base
has_many :defect_root_causes
has_many :defects, :through => :defect_root_causes
end
Controller (I only need changes to one of them for this I think):
# POST /defects
# POST /defects.json
def create
#I am doing this because the attributes are not named :defect_root_cause_attribues, I don't know yet why
#defect = Defect.new(defect_params.except(:defect_root_cause))
#defect.defect_root_cause = DefectRootCause.new(defect_params[:defect_root_cause])
respond_to do |format|
if #defect.save
format.html { redirect_to #defect, notice: 'Defect was successfully created.' }
format.json { render :show, status: :created, location: #defect }
else
format.html { render :new }
format.json { render json: #defect.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /defects/1
# PATCH/PUT /defects/1.json
def update
respond_to do |format|
#I am doing this because the attributes are not named :defect_root_cause_attribues, I don't know yet why
if #defect.update(defect_params.except(:defect_root_cause)) && #defect.defect_root_cause.update(defect_params[:defect_root_cause])
format.html { redirect_to #defect, notice: 'Defect was successfully updated.' }
format.json { render :show, status: :ok, location: #defect }
else
format.html { render :edit }
format.json { render json: #defect.errors, status: :unprocessable_entity }
end
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def defect_params
#I am doing this because the attributes are not named :defect_root_cause_attribues, I don't know yet why
params.require(:defect).permit(:details, defect_root_cause: [:id, :details, :root_cause, :default_root_cause_id])
end
Now the view:
<%= form_for #defect do |f| %>
<% if #defect.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#defect.errors.count, "error") %> prohibited this defect from being saved:</h2>
<ul>
<% #defect.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :details %><br>
<%= f.text_field :details %>
<%= f.fields_for #defect.defect_root_cause do |drc| %>
<%= drc.label :root_cause%>
<%= drc.collection_select(:default_root_cause_id, DefaultRootCause.all, :id, :root_cause, :prompt => true) %>
<%= drc.label :details%>
<%= drc.text_field :details %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In the routes.rb I only have the following:
resources :default_root_causes
resources :defects
resources :defect_root_causes
# You can have the root of your site routed with "root"
root 'defects#index'
Finally, my problem (root_cause is nil):
irb(main):006:0> DefectRootCause.find(10)
DefectRootCause Load (0.3ms) SELECT `defect_root_causes`.* FROM `defect_root_causes` WHERE `defect_root_causes`.`id` = 10 LIMIT 1
=> #<DefectRootCause id: 10, details: "sadsad", defect_id: 9, default_root_cause_id: 2, created_at: "2016-02-19 05:52:06", updated_at: "2016-02-19 05:52:06", root_cause: nil>
if I change the select to:
<%= drc.collection_select(:root_cause, DefaultRootCause.all, :id, :root_cause, :prompt => true) %>
I get the following (root_cause is saved but it is a number not the text - in this case I want the following text: "second root cause", plus the default_root_cause_id is nil):
irb(main):005:0> DefectRootCause.find(9)
DefectRootCause Load (0.3ms) SELECT `defect_root_causes`.* FROM `defect_root_causes` WHERE `defect_root_causes`.`id` = 9 LIMIT 1
=> #<DefectRootCause id: 9, details: "das", defect_id: 8, default_root_cause_id: nil, created_at: "2016-02-19 05:42:36", updated_at: "2016-02-19 05:42:36", root_cause: "2">
EDIT:
Now I am posting what I have done to save all the information where I need it.
I am now posting as an answer because I still don't know if this is the right way of doing this.
So please, if you know it should be done differently let me know. I am looking especially at how to send all the information from the view. I saw the view "knows" both the id and the text of the selected DefaultRootCause, but I don't get both in the params sent.
So, now the code.
View:
<%= drc.select :default_root_cause_id, DefaultRootCause.all.collect{|r| [r.root_cause, r.id]}, :prompt => true %>
defects_controller.rb
def create
#defect = Defect.new(defect_params.except(:defect_root_cause))
#defect.defect_root_cause = DefectRootCause.create(defect_params[:defect_root_cause].merge(:root_cause => DefaultRootCause.find(defect_params[:defect_root_cause][:default_root_cause_id]).root_cause))
respond_to do |format|........
def update
respond_to do |format|
if #defect.update(defect_params.except(:defect_root_cause)) && #defect.defect_root_cause.update(defect_params[:defect_root_cause].merge(:root_cause => DefaultRootCause.find(defect_params[:defect_root_cause][:default_root_cause_id])))
With those I get the following params and also the following inserts:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"jGUQIxeXWZwg5lqsPFrE4vO9z4ioRnAN/Z1/K9scQiIeQ0MTHBTvZoDcwHKtGvrgrG53y4yr7tX4wnCShoZ/oA==", "defect"=>{"details"=>"a", "defect_root_cause"=>{"default_root_cause_id"=>"2", "details"=>"a"}}, "commit"=>"Create Defect"}
DefaultRootCause Load (0.1ms) SELECT `default_root_causes`.* FROM `default_root_causes` WHERE `default_root_causes`.`id` = 2 LIMIT 1
CACHE (0.0ms) SELECT `default_root_causes`.* FROM `default_root_causes` WHERE `default_root_causes`.`id` = 2 LIMIT 1 [["id", "2"]]
CACHE (0.0ms) SELECT `default_root_causes`.* FROM `default_root_causes` WHERE `default_root_causes`.`id` = 2 LIMIT 1 [["id", "2"]]
(0.1ms) BEGIN
SQL (0.2ms) INSERT INTO `defect_root_causes` (`details`, `default_root_cause_id`, **`root_cause`**, `created_at`, `updated_at`) VALUES ('a', 2, **'second root cause'**, '2016-02-23 07:10:59', '2016-02-23 07:10:59')
Now, please let me know if this is "the right" way to do this.
Thank you,
Victor
You are trying to update an associated attributes through the parent which means you need to use accepts_nested_attributes_for. Add this method to your Defect model like so:
class Defect < ActiveRecord::Base
has_one :defect_root_cause, :dependent => :destroy
has_many :default_root_cause, :through => :defect_root_cause
accepts_nested_attributes_for :defect_root_cause
end
After doing that, go to your defects_controller.rb and update the #defect_params method to:
params.require(:defect).permit(:details, defect_root_cause_attributes: [:id, :details, :root_cause, :default_root_cause_id])
You Can Use the following code:
<%=f.select :default_root_cause_id, DefaultRootCause.all.collect{|r| [r.route_cause,"#{r.route_cause} - #{r.id}"]} %>

RoR; Object expected got string

I found several topics related , but couldn't figure out how to solve this problem,
This is my code
<%= f.label :projeto %><br>
<%= f.collection_select :projeto, Projeto.order(:id), :id, :name, include_blank: true %>
model => task belongs_to projeto and projeto has_many tasks
Full Error:
ActiveRecord::AssociationTypeMismatch at /tasks/1
Projeto(#69996814678740) expected, got String(#69996762580840)
Database is set to t.references.
Task_controller
def update
respond_to do |format|
if #task.update(task_params)
format.html { redirect_to #task, notice: 'Task was successfully updated.' }
format.json { render :show, status: :ok, location: #task }
else
format.html { render :edit }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def task_params
params.require(:task).permit(:seq, :descr, :seqpai, :typo, :hour, :projeto)
end
Started PATCH "/tasks/1" for 127.0.0.1 at 2015-06-18 14:30:23 -0300
Processing by TasksController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Is7YTC0v5OONEEsIgOvmI+CEuVYG/WsoKWzskGippD2eOwthKVHb2dI+S19GkkI9aU0IwTrzwERlLq2ybWbGxw==", "task"=>{"seq"=>"0", "descr"=>"Projeto", "seqpai"=>"", "typo"=>"Analitica", "hour"=>"12", "projeto"=>"1"}, "commit"=>"Update Task", "id"=>"1"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = ? LIMIT 1 [["id", 1]]
(0.2ms) begin transaction
(0.1ms) rollback transaction
Completed 500 Internal Server Error in 25ms (ActiveRecord: 0.8ms)
Normally this is caused by assigning a string to something expecting a model association. For example:
# Not allowed, `project=` expects a Project model
#task.project = params[:project_id]
# Converts parameter into model, but may throw exception
#task.project = Project.find(params[:project_id])
# Direct assignment, requires validation on model level
#task.project_id = params[:project_id]
I changed the View for
<%= f.label :projeto_id %>
and permited in controller projeto_id
Then worked Fine,
Thanks everyone for the hint.

Edit inserting new record

I have a Rails 4 Application where I have the following code:
my _form_html.erb
<%= nested_form_for #store, :html => {:multipart => true, :honeypot => true} do |f| %>
<%= f.text_field :name %>
<% if params[:action] == "new" %>
<textarea name="store[products_attributes][0][product_fields_attributes][0][text_content]"></textarea>
<% else %>
<textarea name="store[products_attributes][0][product_fields_attributes][0][text_content]">VALUE</textarea>
<% end %>
<%= f.submit%>
<% end %>
My controller looks like:
before_action :set_store, only: [:show, :edit, :update, :destroy]
def new
#store = Store.new
end
def edit
end
def create
#store = Store.new(store_params)
respond_to do |format|
if #store.save
format.html { redirect_to #store, notice: 'Store was successfully created.'}
format.json { render action: 'show', status: :created, location: #store }
else
format.html { render action: 'new' }
format.json { render json: #store.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #store.update(store_params)
format.html { redirect_to #store, notice: 'Store was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #store.errors, status: :unprocessable_entity }
end
end
def set_store
#store = Store.find(params[:id])
end
def store_params
params.require(:store).permit(:name, products_attributes: [:id, { product_fields_attributes: [:id, :text_content] } ])
end
Also my edit.html.erb looks like:
<h3>Edit</h1>
<%= render 'form' %>
and my new.html.erb looks like:
<h3>Add New</h1>
<%= render 'form' %>
and in my rails console when I click "Update" looks like:
Started PATCH "/stores/sNx92thyjcP_jw" for 127.0.0.1 at 2014-05-27 17:10:46 -0600
Processing by StoresController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"nFUg4ynXYyg99rPPPoa3uO/iHP4LT1XlOz3Vm3Zm4Z0=", "store"=>{"name"=>"Testing", "description"=>"", "products_attributes"=>{"0"=>{"type_of"=>"Book", "product_fields_attributes"=>{"0"=>{"text_content"=>"testing testing testing 1"}}}}}, "commit"=>"Update Store", "token"=>"sNx92thyjcP_jw"}
Site Load (0.7ms) SELECT "stores".* FROM "stores" WHERE "stores"."token" = 'sNx92thyjcP_jw' LIMIT 1
(0.2ms) BEGIN
SQL (0.5ms) INSERT INTO "products" ("created_at", "store_id", "type_of", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Tue, 27 May 2014 23:10:46 UTC +00:00], ["store_id", 102], ["type_of", "Book"], ["updated_at", Tue, 27 May 2014 23:10:46 UTC +00:00]]
SQL (0.7ms) INSERT INTO "product_fields" ("created_at", "text_content", "updated_at", "product_id") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Tue, 27 May 2014 23:10:46 UTC +00:00], ["text_content", "testing testing testing 1"], ["updated_at", Tue, 27 May 2014 23:10:46 UTC +00:00], ["product_id", 111]]
(15.5ms) COMMIT
Redirected to http://localhost:3000/products/sNx92thyjcP_jw
Completed 302 Found in 30ms (ActiveRecord: 17.6ms)
My store model:
class Store < ActiveRecord::Base
before_create :generate_token
has_many :products
accepts_nested_attributes_for :products
def to_param
token
end
private
def generate_token
self.token = loop do
random_token = SecureRandom.urlsafe_base64(10, false)
break random_token unless Store.exists?(token: random_token)
end
end
My product model:
class Product < ActiveRecord::Base
belongs_to :store
has_many :product_fields
accepts_nested_attributes_for :product_fields
end
My product fields model:
class ProductField < ActiveRecord::Base
belongs_to :product
mount_uploader :image_content, ImageUploader
end
But when you go to edit the store, instead of updating, it adds a new record. For example, on the new page, you put in the textarea "Testing 1", and then save. Then you go to the edit page and edit the textarea that says "Testing 1" to be "Testing 2", and click save. Now I have two records: "Testing 1" and "Testing 2".
What is going on here? Thanks for all help!
Ok, for some reason you are using the nested_form_for helper, but you are not using nested fields at all, instead you write your html for the nested textarea manually, with a fixed id [0]? This is why it always creates a new nested field. When saving the store, it will check if the given ids exist, and if not (e.g. id 0 never exists), it will create a new record for it.
Using nested fields in rails is actually pretty simple, you should just write
<%= form_for #store, :html => {:multipart => true, :honeypot => true} do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :products do |product| %>
<%= product.text_area :text_content %>
<% end %>
<%= f.submit%>
<% end %>
You are currently not using any dynamic adding (afaik), so you do not need to use the nested_form_for. From the exmaple, I am assuming you always want just one product?
In your controller you will have to change your new action to also create the initial product to make this work.
def new
#store = Store.new
#store.products.build
end
This will add one empty/new product, which you can then fill in.
You are using nested attributs in the model so when editing the name you're creating a new associated model. The ID of the nested model should not be editable.
Check this documentation:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
It's creating a new instance with new ID.
:update_only
For a one-to-one association, this option allows you to
specify how nested attributes are to be used when an associated record
already exists. In general, an existing record may either be updated
with the new set of attribute values or be replaced by a wholly new
record containing those values. By default the :update_only option is
false and the nested attributes are used to update the existing record
only if they include the record's :id value. Otherwise a new record
will be instantiated and used to replace the existing one. However if
the :update_only option is true, the nested attributes are used to
update the record's attributes always, regardless of whether the :id
is present. The option is ignored for collection associations.
I assumed that my _form_html.erb is actually _form.html.erb partial which you're calling from new and edit view.
Surely your form code is sending request to create action otherwise looking at your code there is no reason it will create new record. You should recheck it. Btw, I dont see any reason to use nested_form_for you can also use form_for field.
Anyway, when you visit the edit action ie /stores/12/edit like path, it should pre-populate all field. check it, may be made mistake in defining routes. Or, you might be sending ajax request in a wrong way. Possibility exist.
One more thing, there is no reason to use if-else condition, since the outcome of both condition seems similar.
<%= nested_form_for #store, :html => {:multipart => true, :honeypot => true} do |f| %>
<%= f.text_field :name %>
<textarea name="store[products_attributes][0][product_fields_attributes][0][text_content]"></textarea>
<%= f.submit%>
<% end %>
Params are not correctly formated, they should have this format:
params = { member: { avatar_attributes: { id: '2', icon: 'sad' } } }
I'ts important to send the id in order to allow rails to search for the associated model. You are sending the "0" as a key instead of value.
Follow this guide to create the form:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
And the documentation about the nested params:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Also an alternative:
http://matthewrobertson.org/blog/2012/09/20/decoupling-rails-forms-from-the-database/

No route matches with Nested Resources

I have two associated tables. Venues and Specials. A venue can have many specials. Once a user has created a venue I wish to allow them to create a special on the venues#index page. By using nested resources I have achieved the desired URL: /venues/5/specials/new.
However, my current code results with: No route matches {:controller=>"specials", :format=>nil}
I'm guessing the error is with my SpecialsController and the def new and def create functions.
I would like the URL to take me to a form page where I can enter new data for the specials
<%= link_to 'Add Special', new_venue_special_path(venue) %>
App1::Application.routes.draw do
resources :venues do
resources :specials
end
def new
#venue = Venue.find(params[:venue_id])
#special = #venue.specials.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #special }
end
end
def create
#venue = Venue.find(params[:venue_id])
#special = #venue.specials.build(params[:special])
respond_to do |format|
if #special.save
format.html { redirect_to #special, notice: 'Special was successfully created.' }
format.json { render json: #special, status: :created, location: #special }
else
format.html { render action: "new" }
format.json { render json: #special.errors, status: :unprocessable_entity }
end
end
end
Backtrace
Started GET "/venues/4/specials/new" for 127.0.0.1 at 2011-12-06 23:36:01 +0200
Processing by SpecialsController#new as HTML
Parameters: {"venue_id"=>"4"}
[1m[36mVenue Load (0.2ms)[0m [1mSELECT "venues".* FROM "venues" WHERE "venues"."id" = $1 LIMIT 1[0m [["id", "4"]]
Rendered specials/_form.html.erb (1.9ms)
Rendered specials/new.html.erb within layouts/application (2.6ms)
Completed 500 Internal Server Error in 97ms
ActionView::Template::Error (No route matches {:controller=>"specials", :format=>nil}):
1: <%= form_for(#special) do |f| %>
2: <% if #special.errors.any? %>
3: <div id="error_explanation">
4: <h2><%= pluralize(#special.errors.count, "error") %> prohibited this special from being saved:</h2>
app/views/specials/_form.html.erb:1:in `_app_views_specials__form_html_erb__2784079234875518470_70162904892440'
app/views/specials/new.html.erb:7:in `_app_views_specials_new_html_erb__115378566176177893_70162906293160'
app/controllers/specials_controller.rb:30:in `new'
Rendered /Users/andrewlynch/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.3/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (0.7ms)
redirect_to #special
this will default to "specials_path", but you're using venue_special_path
you probably want:
redirect_to [#venue, #special]
and in the form you will need the same:
<%= form_for([#venue, #special]) do |f| %>
basically - the issue is that you have a nested resource... which means that every place where you are declaring a url path (including implicit places like form_for) has to be replaced with both the #venue and the #special, instead of just the #special.
you may come across this same "bug" elsewhere in your generated scaffold code... just do the same thing and you should be good.

Resources