Creating view for adding nested resource in Ruby on Rails app - ruby-on-rails

I'm making a RoR app that has 3 resources, that are nested in this order: Projects -> Feeds -> XML_Fields. Projects has_many Feeds, Feeds has_many XML_Fields. My problem is that I am getting stuck on adding to the Show Feeds view the ability to add a new XML_Field. My code is below:
<h2>Add an XML field:</h2>
<%= form_for ([#feed, #feed.xml_fields.build]) do |f| %>
<div class="field">
<%= f.text_area :tag %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Executing this returns a NoMethodError: undefined method 'tag'. Tag is the name of the column in the xml_fields table I created. My xml_fields_controller has this create method:
def create
#xml_field = Feed.find(params[:feed_id]).xml_fields.build(params[:xml_field])
respond_to do |format|
if #xml_field.save
format.html { redirect_to( :back, :notice => 'XML Field was successfully created.') }
format.xml { render :xml => #xml_field, :status => :created, :location => [#xml_field.feed, #xml_field] }
else
format.html { render :action => "new" }
format.xml { render :xml => #xml_field.errors, :status => :unprocessable_entity }
end
end
end
And my routing file looks like this:
resources :projects do
resources :feeds
end
resources :feeds do
resources :xml_fields
end
Any suggestions to make this NoMethodError go away? Thanks.

model XmlField doesn't have tag field in database ;)

Related

Belongs_To / Has Many Relationship

I have the following:
Clients have many Reports and Reports belong to a client.
However on the creation of the Report it is not assigning the client_id into the database, but not sure why?
Am i doing something wrong here?
Client Model
class Client < ActiveRecord::Base
has_many :reports, :dependent => :destroy
end
Report Model
class Report < ActiveRecord::Base
has_attached_file :report
belongs_to :client
end
Client Controller (Update)
# PUT /clients/1
# PUT /clients/1.json
def update
#client = Client.find(params[:id])
respond_to do |format|
if #client.update_attributes(params[:client])
format.html { redirect_to [:admin,#client], :notice => 'Client was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #client.errors, :status => :unprocessable_entity }
end
end
end
Report Controller (Create)
# POST /reports
# POST /reports.json
def create
#report = Report.new(params[:report])
#report.client_id = params[:client][:client_id]
respond_to do |format|
if #report.save
format.html { redirect_to '/admin/clients', :notice => 'Report was successfully created.' }
format.json { render :json => #report, :status => :created, :location => #report }
else
format.html { render :action => "new" }
format.json { render :json => #report.errors, :status => :unprocessable_entity }
end
end
end
Client Edit View
<%= form_for([:admin, #client.reports.build]) do |f| %>
<label class="formlabel">Report Upload</label>
<%= f.file_field :report, :class=>"text-input small-input" %>
<div class="actions">
<br />
<%= f.submit 'Upload', :class => 'button' %>
</div>
<% end %>
Assistance would be appreciated!
I'm curious; because you're using .build in the form_for, the client may already be in the url.
What if you remove:
#report.client_id = params[:client][:client_id]
and submit, what happens then? Because this line is looking incorrectly at the params, so I wonder if you are overwriting that you built in the form_for
Either that, or a hidden field like #Adam said would work.
The client_id doesn't have a related input field in the form on your view. You could add something to your form like:
f.hidden_field :client_id
And then in your controller, set it as:
#report.client_id = params[:report][:client_id]
Alternatively, you could include the client_id in the url.
Stupid Mistake it seems needed to up the end function on the form-for for the client to close it off before opening the form-for the reports.
Then add the field for the client_id and now just hide the field as per Adam suggestion.
Thanks Steph for suggestions as this help me solve this mistake.
Thanks Everyone! :-)

How to avoid the error undefined method 'mail' for nil:NilClass

If I leave the input box blank. I get this error everytime. I don't want it to make new record when it's blank. when not, I want it to make new record.
this input box is nested and the code of controller is written like this to avoid error
def create
# Check if there is any contact info added
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
#girl = Girl.new(params[:girl])
respond_to do |format|
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
end
end
view is like this
<%= form_for(#girl) do |f| %>
....
<div class="field">
<%= f.label :mail %><br />
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
<%= contact.text_field :mail %>
<% end %>
</div>
....
<div class="actions">
<%= f.submit %>
</div>
<% end %>
my model
class Girl < ActiveRecord::Base
has_many :users
has_one :contact
accepts_nested_attributes_for :contact
attr_accessible :id, :name_en, :name_ja, :gender_id, :contact_attributes, :photo, :tag_list
searchable do
text :name_en, :name_ja
text :contact do
contact.mail
end
end
has_attached_file :photo,
:styles => {
:thumb=> "100x100>",
:small => "400x400>" }
acts_as_taggable_on :tags
acts_as_commentable
end
You have to set
#girl = Girl.new
inside your else block, just before
format.html { render action: "new" }
The error happens because you render the new template and inside it the form_for(#girl) gets a nil object - #girl. In order to render the line <%= f.label :mail %><br /> it tries to call the mail method on the given #girl object in order to get its default value. Since the #girl object is nil and not set in the create action before you render the new template you get this error.
UPDATE:
I misunderstood your situation in the answer on the first part of this post. The solution in my opinion is redirecting to the new girl path instead of just rendering the new action. While rendering only renders the view redirecting will make a full-stack request process. Assuming you have the route new_girl_path set you should replace format.html { render action: "new" } with
format.html { redirect_to new_girl_path }
You can run `rake routes and see what named routes you have set.
I problem is the following few lines of code.
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
If mail is empty in user contact you have removed the contact attributes and created only the user object.
So if you call #girl.contact you will get nil.
I don't know why you have removed the contact attributes.If you still want to do it you need to add one more line.
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
#Assuming you have the association like: user has_one contact
#user.build_contact
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
And one more thing
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
can be simply written as
<%= f.fields_for :contact do |contact| %>
Replace same line of code with <%= form_for( :girl, :url => {:action => :create}) do |f| %>

Rails 3 and paperclip on update method issue

Paperclip is working well to save a user avatar but I find an issue on update.
In my view, if the user has an image saved in the model it will show an image tag with the current image next to the file upload field so you can see what your current avatar is.
If the image has no changes but the model validation fails (like no first_name), the original display image disappears, meaning that the user either has to correct the error and re-select an image and submit (update) or go back and start over without the error. Any ideas? Thanks in advance.
Heres the code:
Model
class User < ActiveRecord::Base
# Validation
validates :first_name, :presence => true
# Paperclip
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "70x70#" }
end
Controller
...
# GET /users/1/edit
def edit
#user = User.find(params[:id])
end
# POST /users
# POST /users.xml
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(#user, :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
...
View
<%= form_for #user, :html => {:multipart => true} do |f| %>
<div class="row text">
<%= f.label :first_name %>
<div class="field">
<%= f.text_field :first_name %>
</div>
</div>
<div class="row">
<%= f.label :avatar %>
<div class="field">
<%= image_tag #user.avatar.url(:thumb) %>
</div>
<div class="field" id="avatar_upload">
<%= f.file_field :avatar %>
</div>
</div>
<div class="row actions"><%= f.submit %> or <%= link_to 'cancel', users_path %>.</div>
<% end %>
This is being caused by your form sending 'nil' to the server (due to the field being blank since a new avatar is not being uploaded) when your page sends a POST request to your server. Short of mucking things up with hidden temporary fields, there are a few ways around this:
Client-side validation. Prevent your page from submitting if anything is wrong and you won't end up reaching the server-side validation failure that results in the missing image.
Move the avatar field to a separate FORM object on the same page that only handles the avatar. That will ensure that the page only ever sends avatar information in a POST message if it is related to an actual avatar edit.
Move the avatar upload/update functionality to a completely separate page that only handles the avatar. This works for the same reason the reason above this one works.

Rails: Form for selecting an existing parent when creating new child records?

I have a has_many and belongs_to association set up between two models: Project and Task.
I'd like to be able to create a form which enables me to create a new Task and assign an existing Project as a parent. For example, this form might have a pulldown for selecting from a list of existing projects.
There are only a finite set of projects available in this application, so I've created Project records via a seeds.rb file. I do not need to make a form for creating new Projects.
I believe I've achieved a solution by using a collection_select form helper tag in the new Task form. I'm pretty happy with how this works now, but just curious if there are other approaches to this problem.
#models/project.rb
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
end
#models/task.rb
class Task < ActiveRecord::Base
belongs_to :project
end
#controllers/tasks_controller.rb
class TasksController < ApplicationController
def new
#task = Task.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #task }
end
end
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to(#task, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
else
format.html { render :action => "new" }
format.xml { render :xml => #task.errors, :status => :unprocessable_entity }
end
end
end
end
#views/new.html.erb
<h1>New task</h1>
<%= form_for(#task) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="select">
<%= collection_select(:task, :project_id, Project.all, :id, :name) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', tasks_path %>
I just reviewed your code and this looks fantastic to me. One small tweak:
<%= f.collection_select(:project_id, Project.all, :id, :name) %>
This is just slightly cleaner in that you're still using the |f| block variable
Since you mentioned other approaches, I would definitely mention and actually recommend, you use formtastic. The associations are handled automatically and keeps your code clean and also gives you some great customization options.

Updating user field with current_user when saving nested objects

I'm saving nested objects within the objects they belong to, but when i do that they do not use the controller im saving but the parents controller.
class Project < ActiveRecord::Base
belongs_to :company
belongs_to :user
has_many :tasks
accepts_nested_attributes_for :tasks, :allow_destroy => true
end
in the views i have something like this
<% form_for #project do |c| %>
<% c.fields_for :tasks, #project.tasks.last do |p| %>
<%= p.text_field :name %>
<% end %>
<%= submit_tag '+' %>
<% end %>
so what i'm trying to do, is update the user field with the fields for, that last field is specified in the controller.
def show
#project = Project.find(params[:id])
#project.tasks.build
#project.tasks.last.user = current_user # this should pass to the show.html.erb, to be saved back
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #project }
end
end
I'm thinking maybe the solution would be to check if the username is set in the nested objects, and if not to populate it with the current user in:
def update
#project = Project.find(params[:id])
#project.user = current_user
#find anything #project.....user blank and set to current user
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to(#project, :notice => 'Project was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #project.errors, :status => :unprocessable_entity }
end
end
end
I'm hoping that is the solution, and how do it do it?
an example of it running currently is at http://severe-fire-37.heroku.com

Resources