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.
Related
I have a Profile model and I'm trying to see if that profile has used any other names in the past. I came up with adding a AdditionalName model, and the association is Profile has_many AdditionalName. In the form, I would like for them to fill out up to 5 additional names. I saw from this question I can build it x.times and use f.fields_for :additional_names to show 5 additional names. Problem is, it will save all 5 when I submit the form, even if they don't fill anything. Is it possible that the AdditionalName records only save when they fill out something in the input field?
additional_name.rb
class AdditionalName < ApplicationRecord
belongs_to :profile
end
profile.rb
class Profile < ApplicationRecord
has_many :additional_names
accepts_nested_attributes_for :additional_names
end
controller:
def new
existing_additional_names = #profile.additional_names.count
(5 - existing_additional_names).times do
#current_object.additional_names.build
end
form:
<%= form_with model: #profile, url: wizard_path, method: :patch, local: true do |f| %>
<div class="form-row">
<%= f.fields_for :additional_names do |additional_names_form| %>
<%= additional_names_form.hidden_field :id %>
<div class="form-group col-md-6">
<div class="mx-auto">
<%= additional_names_form.label :name, "Full name" %>
<%= additional_names_form.text_field :name, class: "form-control", id: "ignore-button-disable" %>
</div>
</div>
<% end %>
</div>
<div class="row">
<div class="col-md-6 my-2">
<%= f.submit 'Continue', class: 'btn btn-primary w-100' %>
</div>
</div>
<% end %>
You don't need a custom logic if you just want the nested records to be created only if a certain attribute is set. You can pass option :reject_if to accepts_nested_attributes_for call in the model with a lambda to handle it.
From the definition:
Allows you to specify a Proc or a Symbol pointing to a method that checks whether a record should be built for a certain attribute hash.
accepts_nested_attributes_for :additional_names,
reject_if: -> { |attributes| attributes['name'].blank? }
I just needed to overwrite additional_names_attributes= in the model:
def additional_names_attributes=(additional_name_attributes)
additional_name_attributes.values.each do |additional_name_attribute|
unless additional_name_attribute[:name].empty?
additional_name = AdditionalName.find_or_create_by(additional_name_attribute)
self.additional_names << additional_name
end
end
end
I am using has_nested_attributes_for to create two records of two different models (a Parent and a Child). Currently using has_nested_attributes, my new.html.erb form on the parent successfully creates the parent and child and associates them together. However, the Parent records can have_many of the children model associated to it. Therefore from the new form, I need to be able to enter a url attribute (a column on the parent model) and if the url already exists...It should popup as an already existing Parent (i may use the 'rails-jquery-autocomplete' gem for this if jquery is needed)...thereby setting the existing parent id on the form. If however it does not already exist, the form should create a new parent record and child record as is currently successfully happening.
How would i need to change my controller and view to accomplish this sort of conditional nested form? thanks,
Parent Controller:
class StoriesController < ApplicationController
def new
#story = Story.new
video = #story.videos.build
end
def create
#story = Story.new(story_params)
if #story.save
flash[:success] = "Your story video has been created."
redirect_to current_narrator
else
flash[:alert] = "Your story or video could not be saved. Please include all fields."
render 'new'
end
end
private
def story_params
params.require(:story).permit(:headline, :url, videos_attributes: [
:imovie,
:director_id
],)
end
end
App/Views/Stories new.html.erb:
<!-- New Story Nested Form -->
<% provide(:title, 'New Story') %>
<div class="container s-in-reg">
<div class="authform">
<h1>New Story</h1>
<%= form_for #story do |f| %>
<div class="field">
<%= f.label :headline %><br />
<%= f.text_field :headline %>
</div><br/>
<div class="field">
<%= f.label :url %><br />
<%= f.text_field :url %>
</div><br/>
<%= f.fields_for :videos do |builder| %>
<div class="field">
<%= render 'video_fields', f: builder %>
# Video_fields partial contains the nested video fields required
</div>
<% end %>
<%= f.submit "Post this story", class: "btn btn btn-info" %>
<% end %>
</div>
</div>
Story.RB Model:
has_many :videos
accepts_nested_attributes_for :videos
validates :headline, presence: true
validates :url, presence: true, uniqueness: true
Video.RB Model:
class Video < ActiveRecord::Base
belongs_to :story
belongs_to :user
has_attached_file :mpeg
has_nested_attributes_for :story
end
So what you want to do is to have a child which accepts_nested_attributes_for a parent.
Basically, the easiest solution is to pass a parent_id if you already have a parent you want to associate your child with, or pass parent_attributes if you are just going to create it.
It might require you to manually check the request parameters in the controller and remove unused parameter to avoid confusion. For example, if you pass parent_id you want to completely remove parent_attributes from the returned hash, but if parent_id = nil, then you want to remove parent_id and leave parent_attributes
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| %>
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.
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