Ruby on Rails: Association model, do I need controller to save data? - ruby-on-rails

I'm new to Ruby on Rails, maybe someone can help me.
I have an association, I was wondering if I need a controller to save data into the database table?
I have user.rb model
has_many :businesses
I have business.rb model
belongs_to :user
I have this in the business migration file
class CreateBusinesses < ActiveRecord::Migration
def change
create_table :businesses do |t|
t.integer :user_id
t.string :name
t.string :street
t.string :state
t.string :city
t.integer :zip
t.timestamps
end
end
end
I'm wondering if I need to create a controller file to save data into business table?
I have something like this in views/users/profile.html.erb page
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :company_name %>
<%= f.text_field :company_name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
How do I set this form so that I can save my :company_name into business's table :name as well, and so I can also add :street, :state, etc... to this form?
I only generated a model, and there is no controller for businesses yet.
Thanks!

You don't necessarily need a business controller, but you will need a user controller. You can have your user controller save associated objects for your user by way of nested attributes.
Firstly, allow you user model to accept nested attributes for the business relation:
user.rb
accepts_nested_attributes_for :business
then add fields_for the business object into your user form:
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :company_name %>
<%= f.text_field :company_name %>
</div>
<%= fields_for :business do |fields| %>
<%= fields.text_field :name %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The business attributes will come through as part of the params user => {:name => 'Jim', :business_attributes => {:name => 'Jims business'}}
You can then pass these straight into the update of your user object in the create or update calls of your user controller:
def update
#user = User.find(params[:id])
#user.update_attributes(params)
end
And the business params will be handled by the accepts_nested_attributes functionality!
The example above explains a single instance example, since you have a has_many relation you will need to take the above as a starting point and learn how to adapt it to allow many child items. Below are some resources to help you learn this, rather than giving you the entire code and you not learn anything!
Read more about nested attributes
A handy railscast to follow for multiple child instances, with javascript controls

Of course you need a controller. Not necessarily the same controller, but one is needed.
The controller is needed to connect the view and the model. Without it when you submit your data there is no action to send it. Obviously, the database won't be modified this way. You can't even display your view without an action in the controller.
Models without corresponding containers are only used when it is closely attached some other model, like a forum-comment pair. So you can let the user controller to handle business data, but that is not really recommended.

Related

Store value from another table form in rails

I have a users table and games table. Games table has user_id. The help I want is how can I change/enter the value of city of birth from the game's form which is a field in the user table. I am using the try() method to display the value of city of birth from the user table in the game's form.
user.rb
has_many :games, dependent: :destroy
game.rb
belongs_to :user
_form.html.erb(game)
<div class="custom-hidden field">
<%= form.label :user_id %>
<%= form.number_field :user_id %>
</div>
<div class="field">
<%= form.label :city_of_birth, "User City of Birth" %>
<%= form.text_field :city_of_birth, :value => #user.try(:city_of_birth) %>
</div>
<div class="field">
<%= form.label :game_name %>
<%= form.text_field :game_name %>
</div>
I'm going to answer this question assuming that this form is meant to create a Game, which belongs_to :user, since there is a user_id in the form, and that user.city_of_birth is a string.
The traditional way to do this would be to use theaccepts_nested_attributes_for feature of Rails.
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
However, I would strongly suggest that you consider writing a form object to handle this, so that the responsibility for validating this mixed-model form is held cleanly in one place. I suggest using a gem like Reform to make this process easier.
# app/forms/games/new_game_form.rb
class Games::NewGameForm < Reform::Form
property :user_id, validates: { presence: true }
property :city_of_birth, virtual: true, validates: { presence: true }
property :game_name, validates: { presence: true }
def save
Game.transaction do
user.update!(city_of_birth: city_of_birth)
Game.create(user: user, game_name: game_name)
end
def user
#user ||= User.find(user_id)
end
end
This form object can then be used in place of the Game instance.
# GamesController
def new
#form = Games::NewGameForm.new(Game.new)
end
<!-- new.html.erb -->
<%= form_with #form, url: games_path, method: :post do |f| %>
<!-- your form fields -->
<% end %>
Please note that it appears very odd that you're accepting a user_id in the form, but also reading the city_of_birth from #user. If the #user for whom the game is being created is already known (perhaps the signed in user), then it's useless (and a security risk) to accept the ID in the form - you can simply use the same method that was used to set #user.

Rails nested form not rendering

I'm guessing this is more of a fundamental issue than the form simply "not rendering", but here it goes. I'll try to keep this brief but a fair amount of context may be needed.
I'm making a custom Rolodex app and the organization gave me specific things to include. For this specific problem I'm dealing with contact emails only. Ideally I would have a system like Google Contact's, where you can click to add another email field and there's a dropdown to select a category (Home, Work, etc.).
In this case the categories are coming from a table called categories. Here is a link to the entity relationship diagram I made for the entire project (not just emails): http://i.imgur.com/LNSWZHy.jpg
To sum things up: How do I set things up to allow the entry of emails during a contact creation/edit?
Here's my relevant code:
models/contact.rb
class Contact < ActiveRecord::Base
has_many :emails
accepts_nested_attributes_for :emails
end
models/email.rb
class Email < ActiveRecord::Base
belongs_to :contact
belongs_to :category
end
controllers/contacts_controller.rb
# GET /contacts/new
def new
#contact = Contact.new
#email = #contact.emails.build(params[:email])
end
views/contacts/_form.html.erb
<%= form_for(#contact) do |f| %>
#Other contact fields here
<% f.fields_for #email do |email| %>
<div class="field">
<%= email.label :category_id %><br>
<%= email.text_field :category_id %><br/>
</div>
<div class="field">
<%= email.label :email %><br>
<%= email.text_field :email %><br/>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I also confirmed that this whole setup works "manually". I can make contacts and categories, and then properly reference them when creating a new email by manually putting in the foreign ids. The issue here is a matter of condensing this process into one form.
Any input would be appreciated, thanks!
Change:
<% f.fields_for #email do |email| %>
into
<%= f.fields_for #email do |email| %>

Update fails for nested attributes when nested object is ActiveRecord subclass

I have ActiveRecord with a subclass and its associated with another ActiveRecord object.
I am able to create my object with nested attributes with a form with nested attributes no problem for a new object (following Ryan Bates rails cast - Thanks by the way :)). However when i do an update it fails to save the changes to either the main object or the related object when submitted
I have the following Activerecord classes and sub class.
class Room < ActiveRecord::Base
attr_accessible :name, :type, room_headers_attributes
has_many :room_headers, dependent: :destroy
accepts_nested_attributes_for :room_headers , :allow_destroy => true
end
And the sub class is
class BigRoom < Room
end
And the related class is
class RoomHeader < ActiveRecord::Base
attr_accessible :key, :room_id, :value
belongs_to :room
end
In my room controller I created the nested objects. note that i'm using :type to specify the subclass type
def new
#room = current_user.passes.build(params[:room])
#room.type = params[:type]
3.times do
room_header = #room.room_headers.build
end
....
end
....
def edit
#room = Room.find(params[:id])
end
def update
#room = Room.find(params[:id])
if #room.update_attributes(params[:room])
...
The form used for creating and editing is the same
<%= form_for(#room) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :room_headers do |builder| %>
<%= render 'room_header_fields', f: builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end &>
And the _room_headers.html.erb partial is
<p class="fields">
<%= f.label :key, "Key" %>
<%= f.text_field :key %>
<%= f.label :value, "Value" %>
<%= f.text_field :value %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Header" %>
</p>
To recap on the problem. I can successfully create a new BigRoom. In the new form when i create the BigRoom and I can successfully set values for the RoomHeader class and these are all saved successfully.
However when i Edit the the record and submit changes for update, nothing is saved. Either for changes for the Bigroom attributes or to the associated RoomHeader records.
first try by
if #room.update_attribute(params[:room])
rather
if #room.update_attributes(params[:room])
if this works then their are some errors with your validdations
Ok, nested attributes were a red herring. The problem is with STI
The Rails form helper guide says you can’t rely on record identification with STI.
In the form_for we need to coearce the ids to be the base type id otherwise the edit fails
so
<%= form_for(#room) do |f| %>
should be
<%= form_for(#room.becomes(Room) do |f| %>
if you look at the difference in the html output
the problem html would create ids like big_room_fieldname when in edit mode
when using .becomes we get ids like room_fieldname. in whihc case it saves and updates ok

How to use fields for a nested form outside of the nested form to act as a "global field"?

If I have the models with the following associations:
class Business
has_many :products
class Product
belongs_to :business
And I generate 3 products in the controller:
def new
#business = Business.new
3.times do
#business.products.build
end
end
Making my form look like this:
<%= form_for #business do |f| %>
<% f.text_field :business_name %>
<%= f.fields_for :products do |pf| %> # x2 more products generated
<% pf.text_field :name %>
<% pf.text_field :price %>
<% pf.text_field :date %>
<% end %>
If I want one of the fields to act as a global field for the rest of the products how could I take a field like the :price and put it outside of the f.fields_for :products to have it be the :price for all of the products?
Thank you.
If you need to initialize the price, do it in the controller. But if you need a field that doesn't map to a model directly, use the regular form helpers:
<%= text_field_tag 'global_price' %>
and then in the controller on the create action, it is available as
params[:global_price]
Alternately, you could define a method in your Business model:
def global_price=
#do something with the global price, such as updating child object...
# I'm not sure if the child form objects have been instantiated yet though
end
and then you can use it in your business form:
<%= f.text_field :global_price %>
If you need to update the child objects, you might have to do that at a later time; instead of that method, make it
attr_accessor :global_price
Which makes it an instance variable. Then you can use a before_save filter to update the child objects.
before_save :update_global_price
def update_global_price
#do something with #global_price
end

Rails 3: adding a yes/no "recommended" option to user posts

I'm new to rails and I'm working on a simple app where users create posts with content, hehehe. But since I'm real new I'm having some confusion. When users create a post I want them to have a 'recommended option' yes/no which defaults on the no. So if a user wants to recommend a post he simply selects the yes radio button before he submits the form. I already have the user and post model working to create a post with a title and body. The model relationship is users has_many posts, and posts belongs_to user.
I'd like to keep it really simple and just add a 'recommended' attribute to the post model, using no/yes radio buttons which default to no. I'm confused about the rails form helpers and how to add a yes/no attribute to my post migration. Then how would I select an array of the posts which are recommended by a specific #user?
Thanks a lot!
in the migration:
def self.up
add_column :posts, :is_recommended, :boolean, :default => false
add_column :posts, :message, :text
end
posts_controller.rb:
#rails 2 way:
#recommended_posts = Post.find(:all, :conditions => {:is_recommended => true, :user_id => params[:user_id]})
#rails 3 way:
#recommended_posts = Post.where(:is_recommended => true, :user_id => params[:user_id])
views/posts/new.html.erb: (using check_box rather than radio_button)
<% form_for(#post) do |f| %>
<p>
<%= f.label :message %><br />
<%= f.text_area :message %>
</p>
<p>
<%= f.label 'Recommend' %><br />
<%= f.check_box :is_recommended %>
</p>
<% end %>

Resources