simple_form with two models? - ruby-on-rails

This is my creation form for Customer model.
While populating customers table I am inserting some data in managers table as well. But I want to add a date picker in this simple_form but that date is only stored in Manger model and Customer model doesn't have date field. How do I do it? What alternative options I have?
new.html.erb
<%= stylesheet_link_tag "customers" %>
<div class="row">
<div class="panel panel-default center" id="new-width">
<div class="panel-body">
<%= simple_form_for #customer do |f| %>
<%= f.input :name,:autocomplete => :off %>
<%= f.input :principalAmount,:autocomplete => :off %>
<%= f.input :interestRate %>
<%= f.input :accountType %>
<%= f.input :duration,:autocomplete => :off %>
<%= f.button :submit %>
<% end %>
</div>
</div>
</div>
Edit: Manager model has many field which is independent of Customer model. But When a customer is created it has to add a date in Manager model which is absent in the Customer model.

I suggest you to use accepts_nested_attributes_for in customers model.
Something like this:
In customer model,
accepts_nested_attributes_for :managers
In view page,inside the existing form
<%= f.fields_for :managers do |m| %>
<%= m.date_field :date %>
<% end %>

You can always add a getter and setter to the customer model and manually set the manager fields. Again it depends on the relationship with manager, if it exists already, etc. but the main point is you can create methods that can then be accessed in the form as customer methods.
# in customer.rb
def manager_date=(date)
manager.date = date
end
def manager_date
manager.date
end
then in the form
<%= f.input :manager_date %>
Note - this is a brief example, you'll need to save the manager somewhere and doing this before or after the customer is updated will depend on your needs.
Another way to do this is to create an attr_accessor for manager_date in customer and if it's there, update the manager after the customer is saved
after_save :update_manager
def update_manager
manager.date = manager_date
manager.save
end

Related

How to use nested forms in Rails if the fields have the same name?

I have two models, Dog and Owner, and I want their names to be the same, and would be redundant if I asked to fill out the fields twice (once for the dog and another time for the owner). I'm wondering if there's a simpler way to update the two databases with one input.
<h1>Create a new Dog:</h1>
<%= form_for(#dog) do |f|%>
<div>
<%= f.label :name%>
<%= f.text_field :name%>
</div><br>
<div>
<%= f.label :breed%>
<%= f.text_field :breed%>
</div><br>
<div>
<%= f.label :age%>
<%= f.text_field :age%>
</div><br>
<div>
<h3>create a new owner:</h3>
<%= f.fields_for :owner, Owner.new do |owner_attributes|%>
<%= owner_attributes.label :name, "Owner Name:" %>
<%= owner_attributes.text_field :name %>
<% end %>
</div>
<%= f.submit %>
<% end %>
First of all, not sure why you want to keep the name of the owner and the dog same.
However, there can be many ways to achieve what you want:
You can simply omit the owner name from the form.
So you no longer need: <%= owner_attributes.label :name, "Owner Name:" %>
OR you no longer need:
<div>
<%= f.label :name%>
<%= f.text_field :name%>
</div><br>
And in the Owner/Dog model, you can pass the name of the dog/owner in a callback - maybe after_initialize or before_save or before_validation depending on your validation requirements.
class Dog
belongs_to :owner
before_validation :set_name
private
def set_name
self.name = owner&.name
end
end
You can make the owner name as a hidden field instead and can write some javascript to update the hidden field with the dog name before submitting the form or onblur event. I would prefer the first approach since it's simpler and more secure than only JS solution to maintain database consistency
If dogs belongs_to and owner, you don't really need to store the owner's name separately. You can just call dog.owner.name anywhere you have a Dog instance. Having said that, it is relatively straightforward to append attributes on top of the POSTed form values in your controller using .merge():
def create
#dog = Dog.new(dog_params.merge(owner: params[:dog][:owner])[:name])
if #dog.save
...
end
end
def dog_params
params.require(:dog).permit(:name, :breed, :age, owner: %i[name])
end

Problems using form helpers in ruby on rails

I have a class activity that has the follow atributes:
String type, Date date, String title
By including the associations it also has user_id and place_id.
class Activity < ActiveRecord::Base
belongs_to :user
belongs_to :place
In the other side User has many activities and place has many activities
So, the problem is when I want to create a new activity:
Scaffold creates the helper _form :
<%= form_for(#activity) do |f| %>
<% if #activity.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#activity.errors.count, "error") %> prohibited this activity from being saved:</h2>
<ul>
<% #activity.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :type %><br>
<%= f.text_field :type %>
</div>
<div class="field">
<%= f.label :date %><br>
<%= f.datetime_select :date %>
</div>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :user_id %><br>
<%= f.number_field :user_id %>
</div>
<div class="field">
<%= f.label :place_id %><br>
<%= f.number_field :place_id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I want to receive the first 3 fields from the form (type, date and title) but to associate a user and a place I have to do other way. I need the user that is actual logged in and the place is choosen by tiping the name.
My idea to do this is the following:
1) The user issue, I can make a query by using the current_logged_user that I have acess and get his ID.
2) The place issue, I can use the name that I receive from form and query my Places table for the place with the name X and get the ID after.
But, because I don't know too much about rails, how can I do this? How can I use f.text_field and then made the query or whatever and use after in the controller?
Controller has already this stuff :
def create
#activity = Activity.new(activity_params)
(...)
private
# Use callbacks to share common setup or constraints between actions.
def set_activity
#activity = Activity.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def activity_params
params.require(:activity).permit(:type, :date, :title, :user_id, :place_id)
end
You can structure your rails app to get neither the user_id nor the place_id directly from the form. Especially getting user_id from a submitted form is generally not a good idea. You usually do not want to whitelist user_id at all.
For user_id:
If you are using a gem like devise for user authentication, it gives you access to a method called current_user, which you can use to set the user_id from.
For place_id:
I suggest putting the activity as a sub route of place. e.g. instead of having the form under <host>/activities/new, put it under ``/places/:place_id/activities/new`. In your route file put the route as follows:
resources :places do
resources :activities
end
Now, in your controller action you can do the following:
def create
#activity = current_user.activities.new(activity_params)
#activity.place_id = params[:place_id] (or even safer will be #activity.place = Place.find(params[:place_id], but this will require one more sql query )
(...)
private
# Never trust parameters from the scary internet, only allow the white list through.
def activity_params
params.require(:activity).permit(:type, :date, :title)
end
UPDATE:
If you absolutely want to have the form under /activities/new route then you can have a select tag for place_id in your form:
select_tag 'activity[place_id]', options_from_collection_for_select(Place.all, 'id', 'name')
This will create a selection with name 'activity[place_id]' (named this way for params.require(:activity).permit(place_id) ) and options looking like
<option value="1">Barcelona</option>

Showing name in drop down select box

I want to create a task and assign to a particular user who has_one profile with first name and last name.
And also user has_many tasks.
My new task form is
<%= form_for :task do |f| %>
<div>
<%= f.label :task %>
<%= f.text_area :task %>
</div>
<div>
<%= f.label :assigned_to %>
<%= f.collection_select(:assigned_to, User.profile.all, :fist_name, :fist_name, :include_blank => 'None')%>
</div>
in the collection select I should display something like " Sam Parker (sam#org.com)" like this all the users should be available in the collection select field.
Can anyone help me how to display it.
First of all read the documentation here:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-collection_select
I guess you need to create a method that returns the option's text, for example:
class User < ActiveRecordBase
# ...
def label_for_select
"#{profile.full_name} (#{profile.email})"
end
end
Then, in your view it would be something like:
<div>
<%= f.label :assigned_to_id %>
<%= f.collection_select(:assigned_to_id, User.all, :id, :label_for_select, :include_blank => 'None')%>
</div>
Notice that the form sets the assigned_to_id not the assign_to ruby object. Rails will instantiate the assign_to object based on this id.

Rails 3 - Create View to Insert Multiple Records

I have what seems like a simple query. I need to create a view that will accept multiple records based on a single model. In my case the model is Project, which has 1 foreign key (person) and 2 fields time, role. I need to create a view (form) to insert 5 roles.
<%= form_for(#project) do |f| %>
<% 5.times do |index|%>
<div class="field">
<%= f.label :position %><br />
<%= f.text_field "fields[#{index}][stime]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I get an error message: undefined method `fields[0][stime]'
I do not think the railscasts for nested models is what I need.
How would I go about creating this?
EDIT: The Project model code is below:
class Project < ActiveRecord::Base
belongs_to :person
attr_accessible :role, :stime
end
The Projects_Controller code for the new method is below:
def new
#project = Project.new
end
I see you're planning to make some 1-to-many relationship (Product has_many :roles).
Here's some advices.
First, take a look at the accepts_nested_attributes_for method. You need to add it to your model to be able to perform mass-create.
Second, fields_for is what you need to design nested forms.
I'll give you some example of mass-creating for a simple Product has_many :line_items case:
<%= form_for #product do |f| %>
<%= f.fields_for :line_items, [LineItem.new]*5 do |li_fields| %>
<%= li_fields.text_field :quantity %>
<%= li_fields.text_field :price %>
<br>
<% end %>
<%= f.submit "Create line items" %>
<% end %>
All you need is to write in you controller something like:
#product.update_attributes params[:product]
and 5 line_items will be created at once.
Don't forget to white-list association_attributes (see params in your logs to see it). But I think if you get the mass-assignment error you'll do it anyway :)
I hope it helps.

Multiple forms for the same model in a single page

On the front page of my rap lyrics explanation site, there's a place where users can try explaining a challenging line:
alt text http://dl.dropbox.com/u/2792776/screenshots/2010-02-06_1620.png
Here's the partial I use to generate this:
<div class="stand_alone annotation" data-id="<%= annotation.id %>">
<%= song_link(annotation.song, :class => :title) %>
<span class="needs_exegesis"><%= annotation.referent.strip.gsub(/\n/, "\n <br />") %></span>
<% form_for Feedback.new(:annotation_id => annotation.id, :created_by_id => current_user.try(:id), :email_address => current_user.try(:email)), :url => feedback_index_path, :live_validations => true do |f| %>
<%= f.hidden_field :annotation_id %>
<%= f.hidden_field :created_by_id %>
<p style="margin-top: 1em">
<%= f.text_area :body, :rows => 4, :style => 'width:96%', :example_text => "Enter your explanation" %>
</p>
<p>
<% if current_user %>
<%= f.hidden_field :email_address %>
<% else %>
<%= f.text_field :email_address, :example_text => "Your email address" %>
<% end %>
<%= f.submit "Submit", :class => :button, :style => 'margin-left: .1em;' %>
</p>
<% end %>
</div>
However, putting more than one of these on a single page is problematic because Rails automatically gives each form an ID of new_feedback, and each field an ID like feedback_body (leading to name collisions)
Obviously I could add something like :id => '' to the form and all its fields, but this seems a tad repetitive. What's the best way to do this?
If you don't want to change your input names or your model structure, you can use the id option to make your form ID unique and the namespace option to make your input IDs unique:
<%= form_for Feedback.new(...),
id: "annotation_#{annotation.id}_feedback"
namespace: "annotation_#{annotation.id}" do |f| %>
That way our form ID is unique, i.e. annotation_2_feedback and this will also add a prefix, e.g. annotation_2_, to every input created through f.
Did you consider nested_attributes for rails models? Instead of having multiple new feedback forms where each is tied to an annotation, you could have multiple edit annotation forms where each annotation includes fields for a new feedback. The id's of the generated forms would include the annotations id such as edit_annotation_16.
The annotation model would have a relationship to its feedbacks and will also accept nested attributes for them.
class Annotation < ActiveRecord::Base
has_many :feedbacks
accepts_nested_attributes_for :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :annotation
end
You could then add as many forms as you want, one for each annotation. For example, this is what I tried:
<% form_for #a do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
<% form_for #b do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
And the quick and dirty controller for the above edit view:
class AnnotationsController < ApplicationController
def edit
#a = Annotation.find(1)
#a.feedbacks.build
#b = Annotation.find(2)
#b.feedbacks.build
end
def update
#annotation = Annotation.find(params[:id])
#annotation.update_attributes(params[:annotation])
#annotation.save!
render :index
end
end
I had this same issue on a site I'm currently working on and went with the solution you mention at the bottom. It's not repetitive if you generate the ID programmatically and put the whole form in a partial. For example, on my site, I have multiple "entries" per page, each of which has two voting forms, one to vote up and one to vote down. The record ID for each entry is appended to the DOM ID of its vote forms to make it unique, like so (just shows the vote up button, the vote down button is similar):
<% form_for [entry, Vote.new], :html => { :id => 'new_up_vote_' + entry.id.to_s } do |f| -%>
<%= f.hidden_field :up_vote, :value => 1, :id => 'vote_up_vote_' + entry.id.to_s %>
<%= image_submit_tag('/images/icon_vote_up.png', :id => 'vote_up_vote_submit' + entry.id.to_s, :class => 'vote-button vote-up-button') %>
<% end -%>
I also had the same issue but wanted a more extensible solution than adding the ID to each field. Since we're already using the form_for ... |f| notation the trick is to change the name of the model and you get a new HTML ID prefix.
Using a variant of this method: http://www.dzone.com/snippets/create-classes-runtime (I removed the &block stuff)
I create a new model that is an exact copy of the model I want a second form for on the same page. Then use that new model to render the new form.
If the first form is using
#address = Address.new
then
create_class('AddressNew', Address)
#address_new = AddressNew.new
Your ID prefix will be 'address_new_' instead of 'address_' for the second form of the same model. When you read the form params you can create an Address model to put the values into.
For those stumbling here, looking for the solution for Rails 3.2 app, look at this question instead:
Rails: Using form_for multiple times (DOM ids)

Resources