I'm trying to create a user profile that will have skills, educations, experiences, languages, ... so for this i have this relation
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
after_create :create_profile
private
def create_profile
Profile.create(user: self)
end
end
class Profile < ActiveRecord::Base
belongs_to :user
has_many :profile_languages, dependent: :destroy
accepts_nested_attributes_for :profile_languages , :reject_if => lambda { |a| a[:language_name].blank? }, :allow_destroy => true
has_many :profile_skills, dependent: :destroy
accepts_nested_attributes_for :profile_skills , :reject_if => lambda { |a| a[:skill_name].blank? }, :allow_destroy => true
end
but the problem is after sign up and go to edit the profile the languages and skills will not be saved
this is my profiles controller
def show
#profile = Profile.find(params[:id])
end
def edit
#user = current_user
#profile = #user.profile
end
def update
#user = current_user
#profile = #user.profile
if #profile.update_attributes(profile_params)
redirect_to profile_path
else
flash[:notice] = #profile.errors.full_messages
redirect_to edit_profile_path
end
end
private
def profile_params
params.require(:profile).permit(:user_id, :summary, :profile_title, :useravatar, :profile_skills_attributes: [:id, :skill_name,:_destroy], profile_languages_attributes: [:id, :language, :proficiency, :_destroy] )
end
I was thinking to add a new class in profile controller that will be like this
def new
#profile = current_user.profile.build
#profile.profile_languages.build
#profile.profile_skills.build
end
but i don't think this would be a good solution because i'm already creating the profile after sign up, so i'm wondering what would be the better solution for this
Update
<%= form_for #profile do |f| %>
..................
<%= render 'profile_language_fields', f: f %>
<%= link_to_add_fields "Add languages", f, :profile_languages %>
<%= end %>
and this is profile_language partial
<%= f.fields_for :profile_languages do |pl| %>
<div class="plform-group">
<div class="form-planguage-half">
<%= pl.text_field :language, class: 'form-control form-two-half' %>
</div>
<div class="form-planguage-half-last">
<%= pl.select(:proficiency, [
["Débutant", 1],
["Intermédiaire", 2],
["Courant", 3],
["Bilingue", 4],
["Natif", 5]],
{}, {class: "form-control form-two-half"}) %>
</div>
<%= pl.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</div>
<% end %>
Related
I have two tables class_details and user_classes. user_classes table is dependent of user table and class_details and class_details is independent of user table. Now my requirement is that when i click a button few details must be saved to the database which belong to different tables. User dependent table are only getting saved to database and not the user independent tables and i am getting error undefined method class_detail for nil:NilClass
Controller code
def update_profile
if #user.update(user_params)
redirect_to profile_index_path, notice: 'Profile was successfully updated.'
else
render :index
end
end
end
private
def set_user
#user = User.find(current_user.id)
#user.fee || #user.build_fee
#user.user_class || #user.build_user_class
end
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
end
class_detail.rb
class ClassDetail < ApplicationRecord
has_one :user_class, dependent: :destroy
end
user_class.rb
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
validates_presence_of :user_id
end
user.rb
has_one :fee, dependent: :destroy
accepts_nested_attributes_for :fee
has_one :user_class, dependent: :destroy
view code
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= f.fields_for :class_detail, #user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
Can anyone help me updating user independent table! Thank you in advance
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
That means you want to update class_detail from user_class, but you need to define nested attributes:
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
accepts_nested_attributes_for :class_details, update_only: true
validates_presence_of :user_id
end
Also, the form must look like this:
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= k.fields_for :class_detail, #user.user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
<% end %>
And in your controller:
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: [:id, { class_detail: %i[class_name class_category] }])
end
Edited:
That all means class_details and user_class are associated to the user already. Build references model - child-child-model - child-parent-model from the single call not possible. You can build this references in the edit action. For example:
def edit
#user.create_user_class!(class_detail: ClassDetail.find(n)) unless #user.user_class
end
I've already looked through every other stackoverflow for this issue, but none of the solutions have fixed this. My elements in a nested_form are not being saved in the database. I've also made sure that all model associations are correct. I've been trying to fix this for nearly 8 hours now, and would really appreciate some help, especially considering every other solution hasn't worked.
Basically, I have a Playlist model that contains multiple Song models. I'm trying to use a nested_form to add the Song models to the Playlist. However, none of the Songs are ever being saved. I apologize if my methods are misguides, as I'm still fairly new to Rails.
GitHub Repo:https://github.com/nsalesky/Ultra-Music
playlists_controller.rb
def index
#user = current_user
#playlists = #user.playlists
end
def show
#user = current_user
#playlist = #user.playlists.find(params[:id])
end
def new
#playlist = Playlist.new
#I was told to do this
#playlist.songs.build
end
def create
#user = current_user
#playlist = #user.playlists.create(playlist_params)
if #playlist.save
redirect_to #playlist
else
render :action => 'new'
end
end
def edit
#playlist = current_user.playlists.find(params[:id])
end
def update
#user = current_user
#playlist = #user.playlists.find(params[:id])
if #playlist.update_attributes(playlist_params)
redirect_to #playlist
else
render :action => 'edit'
end
end
def destroy
#user = current_user
#playlist = #user.playlists.find(params[:id])
#playlist.destroy
redirect_to playlists_path(#user.playlists)
end
private
def playlist_params
params.require(:playlist).permit(:name, :description, songs_attributes: [:id, :name, :link, :_destroy])
end
playlist.rb
belongs_to :user
has_many :songs, dependent: :destroy
accepts_nested_attributes_for :songs, :allow_destroy => true, :reject_if => lambda { |a| a[:content].blank? }
validates :name, presence: true
validates_associated :songs, presence: true
_form.html.erb
<%= nested_form_for #playlist do |f| %>
<div>
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<!--<div>
<button type="button" id="addsong">Add Song</button><br>
<button type="button" id="removesong">Remove Song</button><br>
</div> !-->
<div>
<%= f.fields_for :songs do |song_form| %>
<%= song_form.text_field :name %>
<%= song_form.text_field :link %>
<%= song_form.link_to_remove "Remove Song" %>
<% end %>
<p><%= f.link_to_add "Add Song", :songs %></p>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
In your playlist.rb, you wrote:
:reject_if => lambda { |a| a[:content].blank? }
Here the block parameter |a| stands for attributes of a specific song. So a[:attribute] relates to a single attribute. The problem is your Song doesn't have a :content attribute. So this a[:content].blank? will always be true, means you would be rejected building a song.
Just change a[:content] to a valid attribute such as a[:name]
Upon clicking submit only the Duel attributes are passing - not Dueler.
duels_controller.rb
def new
#duel = Duel.new
#user = User.find(params[:challenge_daddy]) # This pulls in the ID for Challenged User
# Current User
#duel.duelers << Dueler.new(user_id: current_user.id, user_name: current_user.name, user_last_name: current_user.last_name)
#current_user_challenges = current_user.challenges.order(:created_at)
# Challenged User
#duel.duelers << Dueler.new(user_id: #user.id, user_name: #user.name, user_last_name: #user.last_name)
#challenged_user_challenges = #user.challenges.order(:created_at)
respond_with(#duel)
end
I think I have to submerge the dueler info (i.e. full_name and collection_select) within something like <%= simple_form_for(#dueler) do |f| %>, but then I don't want two separate submit buttons. When the user clicks submit the dueler and duel information should both submit since they go hand-in-hand. Right now only the duel information submits and the duelers are never created.
duels/_form.html.erb
<%= simple_form_for(#duel) do |f| %>
<%= current_user.full_name %> WILL <%= collection_select(:dueler, :challenge_id, #current_user_challenges, :id, :full_challenge, include_blank: true) %>
<%= #user.full_name %> WILL <%= collection_select(:dueler, :challenge_id, #challenged_user_challenges, :id, :full_challenge, include_blank: true) %>
THE LOSER WILL <%= f.text_field :consequence %>.
<%= f.submit %>
<% end %>
UPDATE
Originally I had this in the _form:
<%= f.fields_for :duelers do |dueler| %>
<%= render 'dueler_fields', :f => dueler %>
<% end %>
But I took it out because the duels_controller new logic wasn't passing into it so I moved the code directly into the _form, but now I'm not sure what should take the place of <%= f.fields_for :duelers do |dueler| %>
class Dueler < ActiveRecord::Base
belongs_to :user
belongs_to :challenge
belongs_to :duel
end
class Duel < ActiveRecord::Base
belongs_to :user
belongs_to :challenge
has_many :duelers
accepts_nested_attributes_for :duelers, :reject_if => :all_blank, :allow_destroy => true #correct
end
class DuelsController < ApplicationController
before_action :set_duel, only: [:show, :edit, :update, :destroy, :duel_request]
respond_to :html
def index
#duels = Duel.joins(:duelers).all
redirect_to duel(#duel)
end
def duel_request
#dueler = #duel.duelers.where(user_id: current_user)
end
def show
#dueler = Dueler.find_by(user_id: current_user.id)
respond_with(#duel)
end
def user_challenges
#user = User.find_by_name(params[:name])
#challenges = #user.challenges.order(:created_at)
end
def new
#duel = Duel.new
#user = User.find(params[:challenge_daddy])
#duel.duelers << Dueler.new(user_id: current_user.id, user_name: current_user.name, user_last_name: current_user.last_name)
#current_user_challenges = current_user.challenges.order(:created_at)
#duel.duelers << Dueler.new(user_id: #user.id, user_name: #user.name, user_last_name: #user.last_name)
#challenged_user_challenges = #user.challenges.order(:created_at)
respond_with(#duel)
end
def edit
end
def create
#duel = Duel.new(duel_params)
#duel.save
#redirect_to duel_request_url(#duel)
respond_with(#duel)
end
def update
#duel.update(duel_params[:duelers_attributes])
respond_with(#duel)
end
def destroy
#duel.destroy
respond_with(#duel)
end
private
def set_duel
#duel = Duel.find(params[:id])
end
def duel_params
params.require(:duel).permit(:consequence, :reward, duelers_attributes: [:id, :user_id, :challenge_id, :accept])
end
end
If you are using has_many and belongs_to with accepts_nested_attributes you will need to use inverse_of to prevent Rails from attempting to lookup records (which of course don't exist because you haven't yet created them)
Change your Duel model has_many declaration to:
has_many :duelers, inverse_of: :duel
For further details on this and an example of a nested form with has_many relationship using Simple Forms check out:
https://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through
Hoping someone can help out with this. I have two models order and date_order. Each order can have multiple date_orders, and I should be able to create many date_orders as I create an order.
How do I do that? As you can see, my code is working well for creating ONE date_order and relating it to the created order.
UPDATE: I have tried to create many "builders" in my orders/new file. It worked on the view, and created an order when I entered multiple dates and times. But the fields_for did not create any date_orders.
orders_controller.rb
def new
#order = Order.new
#order.date_orders.build
end
def create
#order = Order.new(order_params)
if #order.save
flash[:success] = "blah"
redirect_to #order
else
render 'new'
end
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
date_orders_attributes: [:id, :order_date, :time_start, :time_end, :order_id])
end
order.rb
class Order < ActiveRecord::Base
has_many :date_orders, :dependent => :destroy
accepts_nested_attributes_for :date_orders, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
date_order.rb
class DateOrder < ActiveRecord::Base
belongs_to :order
end
order/new.html.erb
<%= form_for(#order, :html => {:multipart => true}) do |f| %>
## SOME QUESTIONS ##
<%= f.fields_for :date_orders do |builder| %>
<%= builder.label :date %>
<%= builder.date_field :order_date %>
<%= builder.label :starting_time %>
<%= builder.time_field :time_start %>
<%= builder.label :ending_time %>
<%= builder.time_field :time_end %>
<% end %>
<% end %>
Build more orders_dates:
class OrdersController < ApplicationController
def new
#order = Order.new
5.times { #order.date_orders.build } # < === HERE ===
end
private
def order_params
params.require(:order).permit(:user_id, :purpose,
# |- === HERE ===
date_orders_attributes: [:id, :content, :order_date, :time_start, :time_end, :order_id])
end
end
Update:
Also, add content to your strong params whitelist.
Im trying to create two hidden fields, and one shows up no problem, but the other that comes from the nested form does not
product.rb
class Product < ActiveRecord::Base
has_many :product_options, dependent: :destroy
accepts_nested_attributes_for :product_options, allow_destroy: true, :reject_if => proc { |x| x[:option_name].blank? }
belongs_to :user
end
product_option.rb
class ProductOption < ActiveRecord::Base
belongs_to :product
end
products_controller.rb
class ProductsController < ActionController::Base
layout "application"
def index
#products = Product.all
#current_user = Client.find_by(id: session[:client])
if #current_user.redeemed == true
redirect_to root_path
end
end
def show
#product = Product.find(params[:id])
#product_option = #product.product_options.find(params[:id])
#current_user = Client.find_by(id: session[:client])
#current_user.update(:product_option => #product_option.option_name)
#current_user.update(:selected_product => #product.id)
render :nothing => true
end
private
def product_params
params.require(:product).permit(:name, :id, :position, :product_description, :product_image_type, :product_image, :product_detail, :product_option_id,
:product_options_attributes => [:id, :option_name, :ranking, :total_redeemed, :product_id])
end
end
_form.html.erb
<%= simple_form_for Product.new, :method => "post",:remote => true, :class => "item_prompt" do |f| %>
<%= f.hidden_field :id, :class => 'product_id' %>
<%= f.simple_fields_for :product_options do |ff| %>
<%= ff.hidden_field :id, :class => 'product_option_id' %>
<% end %>
<%= f.submit "Yep!", :class => "yep ready_button confirm_button", :name => "confirm_button" %>
<% end %>
html output
<form accept-charset="UTF-8" action="/products" class="simple_form new_product" data-remote="true" id="new_product" method="post" novalidate="novalidate">
<input class="product_id" id="product_id" name="id" type="hidden" value="240">
<input class="yep ready_button confirm_button" name="confirm_button" type="submit" value="Yep!">
<form>
I figured this out, ... the problem was
fields_for will loop over a collection association, rendering out as many times as there are items in it, which means 0 times if the association is empty
so to fix the problem I had to add
#product = Product.new
#product.product_options.build
to the index action in the controller.